tap(() => { const fileName = join(outputPath, 'runtime.js'); const content = virtualFs.fileBufferToString(host.scopedSync().read(normalize(fileName))); expect(content).toContain('deployUrl/'); }),
function addAppToWorkspaceFile(options: ApplicationOptions, appDir: string): Rule { let projectRoot = appDir; if (projectRoot) { projectRoot += '/'; } const schematics: JsonObject = {}; if (options.inlineTemplate === true || options.inlineStyle === true || options.style !== Style.Css) { const componentSchematicsOptions: JsonObject = {}; if (options.inlineTemplate === true) { componentSchematicsOptions.inlineTemplate = true; } if (options.inlineStyle === true) { componentSchematicsOptions.inlineStyle = true; } if (options.style && options.style !== Style.Css) { componentSchematicsOptions.style = options.style; } schematics['@schematics/angular:component'] = componentSchematicsOptions; } if (options.skipTests === true) { ['class', 'component', 'directive', 'guard', 'module', 'pipe', 'service'].forEach((type) => { if (!(`@schematics/angular:${type}` in schematics)) { schematics[`@schematics/angular:${type}`] = {}; } (schematics[`@schematics/angular:${type}`] as JsonObject).skipTests = true; }); } const sourceRoot = join(normalize(projectRoot), 'src'); const project = { root: normalize(projectRoot), sourceRoot, projectType: ProjectType.Application, prefix: options.prefix || 'app', schematics, targets: { build: { builder: Builders.Browser, options: { outputPath: `dist/${options.name}`, index: `${sourceRoot}/index.html`, main: `${sourceRoot}/main.ts`, polyfills: `${sourceRoot}/polyfills.ts`, tsConfig: `${projectRoot}tsconfig.app.json`, assets: [ `${sourceRoot}/favicon.ico`, `${sourceRoot}/assets`, ], styles: [ `${sourceRoot}/styles.${options.style}`, ], scripts: [], }, configurations: { production: { fileReplacements: [{ replace: `${sourceRoot}/environments/environment.ts`, with: `${sourceRoot}/environments/environment.prod.ts`, }], optimization: true, outputHashing: 'all', sourceMap: false, extractCss: true, namedChunks: false, aot: true, extractLicenses: true, vendorChunk: false, buildOptimizer: true, budgets: [{ type: 'initial', maximumWarning: '2mb', maximumError: '5mb', }], }, }, }, serve: { builder: Builders.DevServer, options: { browserTarget: `${options.name}:build`, }, configurations: { production: { browserTarget: `${options.name}:build:production`, }, }, }, 'extract-i18n': { builder: Builders.ExtractI18n, options: { browserTarget: `${options.name}:build`, }, }, test: options.minimal ? undefined : { builder: Builders.Karma, options: { main: `${sourceRoot}/test.ts`, polyfills: `${sourceRoot}/polyfills.ts`, tsConfig: `${projectRoot}tsconfig.spec.json`, karmaConfig: `${projectRoot}karma.conf.js`, assets: [ `${sourceRoot}/favicon.ico`, `${sourceRoot}/assets`, ], styles: [ `${sourceRoot}/styles.${options.style}`, ], scripts: [], }, }, lint: { builder: Builders.TsLint, options: { tsConfig: [ `${projectRoot}tsconfig.app.json`, `${projectRoot}tsconfig.spec.json`, ], exclude: [ '**/node_modules/**', ], }, }, }, }; return updateWorkspace(workspace => { if (workspace.projects.size === 0) { workspace.extensions.defaultProject = options.name; } workspace.projects.add({ name: options.name, ...project, }); }); }
tap(() => { const fileName = join(outputPath, 'stats.json'); expect(host.scopedSync().exists(fileName)).toBe(true); }),
private _resolve(path: string): Path { return join(normalize(this.base), path); }
/** * @license * Copyright Google Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ import { TargetSpecifier } from '@angular-devkit/architect'; import { TestProjectHost, runTargetSpec } from '@angular-devkit/architect/testing'; import { join, normalize } from '@angular-devkit/core'; import { tap } from 'rxjs/operators'; const devkitRoot = normalize((global as any)._DevKitRoot); // tslint:disable-line:no-any const workspaceRoot = join(devkitRoot, 'tests/@angular_devkit/build_ng_packagr/ng-packaged/'); export const host = new TestProjectHost(workspaceRoot); export enum Timeout { Basic = 30000, Standard = Basic * 1.5, } describe('NgPackagr Builder', () => { beforeEach(done => host.initialize().toPromise().then(done, done.fail)); afterEach(done => host.restore().toPromise().then(done, done.fail)); it('works', (done) => { const targetSpec: TargetSpecifier = { project: 'lib', target: 'build' }; runTargetSpec(host, targetSpec).pipe(
export function augmentAppWithServiceWorker( host: virtualFs.Host, projectRoot: Path, appRoot: Path, outputPath: Path, baseHref: string, ngswConfigPath?: string, ): Promise<void> { // Path to the worker script itself. const distPath = normalize(outputPath); const workerPath = normalize( resolveProjectModule(getSystemPath(projectRoot), '@angular/service-worker/ngsw-worker.js'), ); const swConfigPath = resolveProjectModule( getSystemPath(projectRoot), '@angular/service-worker/config', ); const safetyPath = join(dirname(workerPath), 'safety-worker.js'); const configPath = ngswConfigPath as Path || join(appRoot, 'ngsw-config.json'); return host.exists(configPath).pipe( switchMap(exists => { if (!exists) { throw new Error(tags.oneLine` Error: Expected to find an ngsw-config.json configuration file in the ${appRoot} folder. Either provide one or disable Service Worker in your angular.json configuration file.`, ); } return host.read(configPath) as Observable<virtualFs.FileBuffer>; }), map(content => JSON.parse(virtualFs.fileBufferToString(content))), switchMap(configJson => { const Generator = require(swConfigPath).Generator; const gen = new Generator(new CliFilesystem(host, outputPath), baseHref); return gen.process(configJson); }), switchMap(output => { const manifest = JSON.stringify(output, null, 2); return host.read(workerPath).pipe( switchMap(workerCode => { return merge( host.write(join(distPath, 'ngsw.json'), virtualFs.stringToFileBuffer(manifest)), host.write(join(distPath, 'ngsw-worker.js'), workerCode), ) as Observable<void>; }), ); }), switchMap(() => host.exists(safetyPath)), // If @angular/service-worker has the safety script, copy it into two locations. switchMap(exists => { if (!exists) { return of<void>(undefined); } return host.read(safetyPath).pipe( switchMap(safetyCode => { return merge( host.write(join(distPath, 'worker-basic.min.js'), safetyCode), host.write(join(distPath, 'safety-worker.js'), safetyCode), ) as Observable<void>; }), ); }), // Remove all elements, reduce them to a single emit. reduce(() => {}), ).toPromise(); }
switchMap(safetyCode => { return merge( host.write(join(distPath, 'worker-basic.min.js'), safetyCode), host.write(join(distPath, 'safety-worker.js'), safetyCode), ) as Observable<void>; }),
describe('Extract i18n Target', () => { const extractionFile = join(normalize('src'), 'messages.xlf'); beforeEach(done => host.initialize().toPromise().then(done, done.fail)); afterEach(done => host.restore().toPromise().then(done, done.fail)); it('works', (done) => { host.appendToFile('src/app/app.component.html', '<p i18n>i18n test</p>'); runTargetSpec(host, extractI18nTargetSpec).pipe( tap((buildEvent) => expect(buildEvent.success).toBe(true)), tap(() => { expect(host.scopedSync().exists((extractionFile))).toBe(true); expect(virtualFs.fileBufferToString(host.scopedSync().read(extractionFile))) .toMatch(/i18n test/); }), ).toPromise().then(done, done.fail); }, 30000); it('shows errors', (done) => { const logger = new TestLogger('i18n-errors'); host.appendToFile('src/app/app.component.html', '<p i18n>Hello world <span i18n>inner</span></p>'); runTargetSpec(host, extractI18nTargetSpec, {}, logger).pipe( tap((buildEvent) => { expect(buildEvent.success).toBe(false); const msg = 'Could not mark an element as translatable inside a translatable section'; expect(logger.includes(msg)).toBe(true); }), ).toPromise().then(done, done.fail); }, 30000); it('supports locale', (done) => { host.appendToFile('src/app/app.component.html', '<p i18n>i18n test</p>'); const overrides = { i18nLocale: 'fr' }; runTargetSpec(host, extractI18nTargetSpec, overrides).pipe( tap((buildEvent) => expect(buildEvent.success).toBe(true)), tap(() => { expect(host.scopedSync().exists((extractionFile))).toBe(true); expect(virtualFs.fileBufferToString(host.scopedSync().read(extractionFile))) .toContain('source-language="fr"'); }), ).toPromise().then(done, done.fail); }, 30000); it('supports out file', (done) => { host.appendToFile('src/app/app.component.html', '<p i18n>i18n test</p>'); const outFile = 'messages.fr.xlf'; const extractionFile = join(normalize('src'), outFile); const overrides = { outFile }; runTargetSpec(host, extractI18nTargetSpec, overrides).pipe( tap((buildEvent) => expect(buildEvent.success).toBe(true)), tap(() => { expect(host.scopedSync().exists(extractionFile)).toBe(true); expect(virtualFs.fileBufferToString(host.scopedSync().read(extractionFile))) .toMatch(/i18n test/); }), ).toPromise().then(done, done.fail); }, 30000); it('supports output path', (done) => { host.appendToFile('src/app/app.component.html', '<p i18n>i18n test</p>'); // Note: this folder will not be created automatically. It must exist beforehand. const outputPath = 'app'; const extractionFile = join(normalize('src'), outputPath, 'messages.xlf'); const overrides = { outputPath }; runTargetSpec(host, extractI18nTargetSpec, overrides).pipe( tap((buildEvent) => expect(buildEvent.success).toBe(true)), tap(() => { expect(host.scopedSync().exists(extractionFile)).toBe(true); expect(virtualFs.fileBufferToString(host.scopedSync().read(extractionFile))) .toMatch(/i18n test/); }), ).toPromise().then(done, done.fail); }, 30000); it('supports i18n format', (done) => { host.appendToFile('src/app/app.component.html', '<p i18n>i18n test</p>'); const extractionFile = join(normalize('src'), 'messages.xmb'); const overrides = { i18nFormat: 'xmb' }; runTargetSpec(host, extractI18nTargetSpec, overrides).pipe( tap((buildEvent) => expect(buildEvent.success).toBe(true)), tap(() => { expect(host.scopedSync().exists(extractionFile)).toBe(true); expect(virtualFs.fileBufferToString(host.scopedSync().read(extractionFile))) .toMatch(/i18n test/); }), ).toPromise().then(done, done.fail); }, 30000); });
return async (host: Tree) => { const workspace = await getWorkspace(host); if (!options.project) { throw new SchematicsException('Option "project" is required.'); } if (!options.target) { throw new SchematicsException('Option "target" is required.'); } const project = workspace.projects.get(options.project); if (!project) { throw new SchematicsException(`Invalid project name (${options.project})`); } const projectType = project.extensions['projectType']; if (projectType !== 'application') { throw new SchematicsException(`Web Worker requires a project type of "application".`); } const projectTarget = project.targets.get(options.target); if (!projectTarget) { throw new Error(`Target is not defined for this project.`); } const projectTargetOptions = (projectTarget.options || {}) as unknown as BrowserBuilderOptions; if (options.path === undefined) { options.path = buildDefaultPath(project); } const parsedPath = parseName(options.path, options.name); options.name = parsedPath.name; options.path = parsedPath.path; const root = project.root || ''; const needWebWorkerConfig = !projectTargetOptions.webWorkerTsConfig; if (needWebWorkerConfig) { const workerConfigPath = join(normalize(root), 'tsconfig.worker.json'); projectTargetOptions.webWorkerTsConfig = workerConfigPath; // add worker tsconfig to lint architect target const lintTarget = project.targets.get('lint'); if (lintTarget) { const lintOptions = (lintTarget.options || {}) as unknown as LintBuilderOptions; lintOptions.tsConfig = (lintOptions.tsConfig || []).concat(workerConfigPath); } } const templateSource = apply(url('./files/worker'), [ applyTemplates({ ...options, ...strings }), move(parsedPath.path), ]); return chain([ // Add project configuration. needWebWorkerConfig ? addConfig(options, root, projectTargetOptions.tsConfig) : noop(), needWebWorkerConfig ? updateWorkspace(workspace) : noop(), // Create the worker in a sibling module. options.snippet ? addSnippet(options) : noop(), // Add the worker. mergeWith(templateSource), ]); };
return this._base.visit((path, entry) => { visitor( join(NormalizedRoot, relative(this.scope, path)), entry && new ScopedFileEntry(entry, this.scope), ); });