@angular-devkit/build-angular 15.0.2 → 15.1.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -34,16 +34,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
34
34
  };
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.createCompilerPlugin = exports.SourceFileCache = void 0;
37
- const core_1 = require("@babel/core");
38
37
  const assert = __importStar(require("node:assert"));
39
- const fs = __importStar(require("node:fs/promises"));
40
38
  const node_os_1 = require("node:os");
41
39
  const path = __importStar(require("node:path"));
42
40
  const node_url_1 = require("node:url");
43
41
  const typescript_1 = __importDefault(require("typescript"));
44
- const application_1 = __importDefault(require("../../babel/presets/application"));
45
- const webpack_loader_1 = require("../../babel/webpack-loader");
46
- const load_esm_1 = require("../../utils/load-esm");
42
+ const environment_options_1 = require("../../utils/environment-options");
43
+ const angular_compilation_1 = require("./angular-compilation");
44
+ const javascript_transformer_1 = require("./javascript-transformer");
47
45
  const profiling_1 = require("./profiling");
48
46
  const stylesheets_1 = require("./stylesheets");
49
47
  /**
@@ -51,11 +49,11 @@ const stylesheets_1 = require("./stylesheets");
51
49
  * Related information is a subset of a full TypeScript Diagnostic and also used for diagnostic
52
50
  * notes associated with the main Diagnostic.
53
51
  * @param info The TypeScript diagnostic relative information to convert.
54
- * @param host A TypeScript FormatDiagnosticsHost instance to use during conversion.
55
52
  * @returns An esbuild diagnostic message as a PartialMessage object
56
53
  */
57
- function convertTypeScriptDiagnosticInfo(info, host, textPrefix) {
58
- let text = typescript_1.default.flattenDiagnosticMessageText(info.messageText, host.getNewLine());
54
+ function convertTypeScriptDiagnosticInfo(info, textPrefix) {
55
+ const newLine = (0, node_os_1.platform)() === 'win32' ? '\r\n' : '\n';
56
+ let text = typescript_1.default.flattenDiagnosticMessageText(info.messageText, newLine);
59
57
  if (textPrefix) {
60
58
  text = textPrefix + text;
61
59
  }
@@ -87,10 +85,9 @@ function convertTypeScriptDiagnosticInfo(info, host, textPrefix) {
87
85
  /**
88
86
  * Converts a TypeScript Diagnostic message into an esbuild compatible message object.
89
87
  * @param diagnostic The TypeScript diagnostic to convert.
90
- * @param host A TypeScript FormatDiagnosticsHost instance to use during conversion.
91
88
  * @returns An esbuild diagnostic message as a PartialMessage object
92
89
  */
93
- function convertTypeScriptDiagnostic(diagnostic, host) {
90
+ function convertTypeScriptDiagnostic(diagnostic) {
94
91
  var _a;
95
92
  let codePrefix = 'TS';
96
93
  let code = `${diagnostic.code}`;
@@ -100,12 +97,12 @@ function convertTypeScriptDiagnostic(diagnostic, host) {
100
97
  code = code.slice(3);
101
98
  }
102
99
  const message = {
103
- ...convertTypeScriptDiagnosticInfo(diagnostic, host, `${codePrefix}${code}: `),
100
+ ...convertTypeScriptDiagnosticInfo(diagnostic, `${codePrefix}${code}: `),
104
101
  // Store original diagnostic for reference if needed downstream
105
102
  detail: diagnostic,
106
103
  };
107
104
  if ((_a = diagnostic.relatedInformation) === null || _a === void 0 ? void 0 : _a.length) {
108
- message.notes = diagnostic.relatedInformation.map((info) => convertTypeScriptDiagnosticInfo(info, host));
105
+ message.notes = diagnostic.relatedInformation.map((info) => convertTypeScriptDiagnosticInfo(info));
109
106
  }
110
107
  return message;
111
108
  }
@@ -133,7 +130,6 @@ class SourceFileCache extends Map {
133
130
  }
134
131
  }
135
132
  exports.SourceFileCache = SourceFileCache;
136
- // This is a non-watch version of the compiler code from `@ngtools/webpack` augmented for esbuild
137
133
  // eslint-disable-next-line max-lines-per-function
138
134
  function createCompilerPlugin(pluginOptions, styleOptions) {
139
135
  return {
@@ -143,11 +139,9 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
143
139
  var _a, _b;
144
140
  var _c;
145
141
  let setupWarnings;
146
- // This uses a wrapped dynamic import to load `@angular/compiler-cli` which is ESM.
147
- // Once TypeScript provides support for retaining dynamic imports this workaround can be dropped.
148
- const { GLOBAL_DEFS_FOR_TERSER_WITH_AOT, NgtscProgram, OptimizeFor, readConfiguration } = await (0, load_esm_1.loadEsmModule)('@angular/compiler-cli');
149
- // Temporary deep import for transformer support
150
- const { mergeTransformers, replaceBootstrap, } = require('@ngtools/webpack/src/ivy/transformation');
142
+ // Initialize a worker pool for JavaScript transformations
143
+ const javascriptTransformer = new javascript_transformer_1.JavaScriptTransformer(pluginOptions, environment_options_1.maxWorkers);
144
+ const { GLOBAL_DEFS_FOR_TERSER_WITH_AOT, readConfiguration } = await angular_compilation_1.AngularCompilation.loadCompilerCli();
151
145
  // Setup defines based on the values provided by the Angular compiler-cli
152
146
  (_a = (_c = build.initialOptions).define) !== null && _a !== void 0 ? _a : (_c.define = {});
153
147
  for (const [key, value] of Object.entries(GLOBAL_DEFS_FOR_TERSER_WITH_AOT)) {
@@ -198,11 +192,9 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
198
192
  let fileEmitter;
199
193
  // The stylesheet resources from component stylesheets that will be added to the build results output files
200
194
  let stylesheetResourceFiles;
201
- let previousBuilder;
202
- let previousAngularProgram;
203
- const babelDataCache = new Map();
204
- const diagnosticCache = new WeakMap();
195
+ let compilation;
205
196
  build.onStart(async () => {
197
+ var _a;
206
198
  const result = {
207
199
  warnings: setupWarnings,
208
200
  };
@@ -212,111 +204,50 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
212
204
  (0, profiling_1.resetCumulativeDurations)();
213
205
  // Reset stylesheet resource output files
214
206
  stylesheetResourceFiles = [];
215
- // Create TypeScript compiler host
216
- const host = typescript_1.default.createIncrementalCompilerHost(compilerOptions);
217
- // Temporarily process external resources via readResource.
218
- // The AOT compiler currently requires this hook to allow for a transformResource hook.
219
- // Once the AOT compiler allows only a transformResource hook, this can be reevaluated.
220
- host.readResource = async function (fileName) {
221
- var _a, _b, _c;
222
- // Template resources (.html/.svg) files are not bundled or transformed
223
- if (fileName.endsWith('.html') || fileName.endsWith('.svg')) {
224
- return (_a = this.readFile(fileName)) !== null && _a !== void 0 ? _a : '';
225
- }
226
- const { contents, resourceFiles, errors, warnings } = await (0, stylesheets_1.bundleStylesheetFile)(fileName, styleOptions);
227
- ((_b = result.errors) !== null && _b !== void 0 ? _b : (result.errors = [])).push(...errors);
228
- ((_c = result.warnings) !== null && _c !== void 0 ? _c : (result.warnings = [])).push(...warnings);
229
- stylesheetResourceFiles.push(...resourceFiles);
230
- return contents;
231
- };
232
- // Add an AOT compiler resource transform hook
233
- host.transformResource = async function (data, context) {
234
- var _a, _b, _c;
235
- // Only inline style resources are transformed separately currently
236
- if (context.resourceFile || context.type !== 'style') {
237
- return null;
238
- }
239
- // The file with the resource content will either be an actual file (resourceFile)
240
- // or the file containing the inline component style text (containingFile).
241
- const file = (_a = context.resourceFile) !== null && _a !== void 0 ? _a : context.containingFile;
242
- const { contents, resourceFiles, errors, warnings } = await (0, stylesheets_1.bundleStylesheetText)(data, {
243
- resolvePath: path.dirname(file),
244
- virtualName: file,
245
- }, styleOptions);
246
- ((_b = result.errors) !== null && _b !== void 0 ? _b : (result.errors = [])).push(...errors);
247
- ((_c = result.warnings) !== null && _c !== void 0 ? _c : (result.warnings = [])).push(...warnings);
248
- stylesheetResourceFiles.push(...resourceFiles);
249
- return { content: contents };
207
+ // Create Angular compiler host options
208
+ const hostOptions = {
209
+ fileReplacements: pluginOptions.fileReplacements,
210
+ modifiedFiles: (_a = pluginOptions.sourceFileCache) === null || _a === void 0 ? void 0 : _a.modifiedFiles,
211
+ sourceFileCache: pluginOptions.sourceFileCache,
212
+ async transformStylesheet(data, containingFile, stylesheetFile) {
213
+ var _a, _b;
214
+ // Stylesheet file only exists for external stylesheets
215
+ const filename = stylesheetFile !== null && stylesheetFile !== void 0 ? stylesheetFile : containingFile;
216
+ // Temporary workaround for lack of virtual file support in the Sass plugin.
217
+ // External Sass stylesheets are transformed using the file instead of the already read content.
218
+ let stylesheetResult;
219
+ if (filename.endsWith('.scss') || filename.endsWith('.sass')) {
220
+ stylesheetResult = await (0, stylesheets_1.bundleStylesheetFile)(filename, styleOptions);
221
+ }
222
+ else {
223
+ stylesheetResult = await (0, stylesheets_1.bundleStylesheetText)(data, {
224
+ resolvePath: path.dirname(filename),
225
+ virtualName: filename,
226
+ }, styleOptions);
227
+ }
228
+ const { contents, resourceFiles, errors, warnings } = stylesheetResult;
229
+ ((_a = result.errors) !== null && _a !== void 0 ? _a : (result.errors = [])).push(...errors);
230
+ ((_b = result.warnings) !== null && _b !== void 0 ? _b : (result.warnings = [])).push(...warnings);
231
+ stylesheetResourceFiles.push(...resourceFiles);
232
+ return contents;
233
+ },
250
234
  };
251
- // Temporary deep import for host augmentation support
252
- const { augmentHostWithCaching, augmentHostWithReplacements, augmentProgramWithVersioning, } = require('@ngtools/webpack/src/ivy/host');
253
- // Augment TypeScript Host for file replacements option
254
- if (pluginOptions.fileReplacements) {
255
- augmentHostWithReplacements(host, pluginOptions.fileReplacements);
256
- }
257
- // Augment TypeScript Host with source file caching if provided
258
- if (pluginOptions.sourceFileCache) {
259
- augmentHostWithCaching(host, pluginOptions.sourceFileCache);
260
- // Allow the AOT compiler to request the set of changed templates and styles
261
- host.getModifiedResourceFiles = function () {
262
- var _a;
263
- return (_a = pluginOptions.sourceFileCache) === null || _a === void 0 ? void 0 : _a.modifiedFiles;
264
- };
265
- }
266
- // Create the Angular specific program that contains the Angular compiler
267
- const angularProgram = (0, profiling_1.profileSync)('NG_CREATE_PROGRAM', () => new NgtscProgram(rootNames, compilerOptions, host, previousAngularProgram));
268
- previousAngularProgram = angularProgram;
269
- const angularCompiler = angularProgram.compiler;
270
- const typeScriptProgram = angularProgram.getTsProgram();
271
- augmentProgramWithVersioning(typeScriptProgram);
272
- const builder = typescript_1.default.createEmitAndSemanticDiagnosticsBuilderProgram(typeScriptProgram, host, previousBuilder, configurationDiagnostics);
273
- previousBuilder = builder;
274
- await (0, profiling_1.profileAsync)('NG_ANALYZE_PROGRAM', () => angularCompiler.analyzeAsync());
275
- const affectedFiles = (0, profiling_1.profileSync)('NG_FIND_AFFECTED', () => findAffectedFiles(builder, angularCompiler));
235
+ // Create new compilation if first build; otherwise, use existing for rebuilds
236
+ compilation !== null && compilation !== void 0 ? compilation : (compilation = new angular_compilation_1.AngularCompilation());
237
+ // Initialize the Angular compilation for the current build.
238
+ // In watch mode, previous build state will be reused.
239
+ const { affectedFiles } = await compilation.initialize(rootNames, compilerOptions, hostOptions, configurationDiagnostics);
240
+ // Clear affected files from the cache (if present)
276
241
  if (pluginOptions.sourceFileCache) {
277
242
  for (const affected of affectedFiles) {
278
243
  pluginOptions.sourceFileCache.typeScriptFileCache.delete((0, node_url_1.pathToFileURL)(affected.fileName).href);
279
244
  }
280
245
  }
281
- function* collectDiagnostics() {
282
- // Collect program level diagnostics
283
- yield* builder.getConfigFileParsingDiagnostics();
284
- yield* angularCompiler.getOptionDiagnostics();
285
- yield* builder.getOptionsDiagnostics();
286
- yield* builder.getGlobalDiagnostics();
287
- // Collect source file specific diagnostics
288
- const optimizeFor = affectedFiles.size > 1 ? OptimizeFor.WholeProgram : OptimizeFor.SingleFile;
289
- for (const sourceFile of builder.getSourceFiles()) {
290
- if (angularCompiler.ignoreForDiagnostics.has(sourceFile)) {
291
- continue;
292
- }
293
- // TypeScript will use cached diagnostics for files that have not been
294
- // changed or affected for this build when using incremental building.
295
- yield* (0, profiling_1.profileSync)('NG_DIAGNOSTICS_SYNTACTIC', () => builder.getSyntacticDiagnostics(sourceFile), true);
296
- yield* (0, profiling_1.profileSync)('NG_DIAGNOSTICS_SEMANTIC', () => builder.getSemanticDiagnostics(sourceFile), true);
297
- // Declaration files cannot have template diagnostics
298
- if (sourceFile.isDeclarationFile) {
299
- continue;
300
- }
301
- // Only request Angular template diagnostics for affected files to avoid
302
- // overhead of template diagnostics for unchanged files.
303
- if (affectedFiles.has(sourceFile)) {
304
- const angularDiagnostics = (0, profiling_1.profileSync)('NG_DIAGNOSTICS_TEMPLATE', () => angularCompiler.getDiagnosticsForFile(sourceFile, optimizeFor), true);
305
- diagnosticCache.set(sourceFile, angularDiagnostics);
306
- yield* angularDiagnostics;
307
- }
308
- else {
309
- const angularDiagnostics = diagnosticCache.get(sourceFile);
310
- if (angularDiagnostics) {
311
- yield* angularDiagnostics;
312
- }
313
- }
314
- }
315
- }
316
246
  (0, profiling_1.profileSync)('NG_DIAGNOSTICS_TOTAL', () => {
317
247
  var _a, _b;
318
- for (const diagnostic of collectDiagnostics()) {
319
- const message = convertTypeScriptDiagnostic(diagnostic, host);
248
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
249
+ for (const diagnostic of compilation.collectDiagnostics()) {
250
+ const message = convertTypeScriptDiagnostic(diagnostic);
320
251
  if (diagnostic.category === typescript_1.default.DiagnosticCategory.Error) {
321
252
  ((_a = result.errors) !== null && _a !== void 0 ? _a : (result.errors = [])).push(message);
322
253
  }
@@ -325,13 +256,11 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
325
256
  }
326
257
  }
327
258
  });
328
- fileEmitter = createFileEmitter(builder, mergeTransformers(angularCompiler.prepareEmit().transformers, {
329
- before: [replaceBootstrap(() => builder.getProgram().getTypeChecker())],
330
- }), (sourceFile) => angularCompiler.incrementalCompilation.recordSuccessfulEmit(sourceFile));
259
+ fileEmitter = compilation.createFileEmitter();
331
260
  return result;
332
261
  });
333
262
  build.onLoad({ filter: compilerOptions.allowJs ? /\.[cm]?[jt]sx?$/ : /\.[cm]?tsx?$/ }, (args) => (0, profiling_1.profileAsync)('NG_EMIT_TS*', async () => {
334
- var _a, _b, _c, _d, _e, _f;
263
+ var _a, _b, _c, _d, _e;
335
264
  assert.ok(fileEmitter, 'Invalid plugin execution order');
336
265
  const request = (_b = (_a = pluginOptions.fileReplacements) === null || _a === void 0 ? void 0 : _a[args.path]) !== null && _b !== void 0 ? _b : args.path;
337
266
  // The filename is currently used as a cache key. Since the cache is memory only,
@@ -341,7 +270,7 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
341
270
  let contents = (_c = pluginOptions.sourceFileCache) === null || _c === void 0 ? void 0 : _c.typeScriptFileCache.get((0, node_url_1.pathToFileURL)(request).href);
342
271
  if (contents === undefined) {
343
272
  const typescriptResult = await fileEmitter(request);
344
- if (!typescriptResult) {
273
+ if (!(typescriptResult === null || typescriptResult === void 0 ? void 0 : typescriptResult.content)) {
345
274
  // No TS result indicates the file is not part of the TypeScript program.
346
275
  // If allowJs is enabled and the file is JS then defer to the next load hook.
347
276
  if (compilerOptions.allowJs && /\.[cm]?js$/.test(request)) {
@@ -354,18 +283,8 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
354
283
  ],
355
284
  };
356
285
  }
357
- const data = (_e = typescriptResult.content) !== null && _e !== void 0 ? _e : '';
358
- // The pre-transformed data is used as a cache key. Since the cache is memory only,
359
- // the options cannot change and do not need to be represented in the key. If the
360
- // cache is later stored to disk, then the options that affect transform output
361
- // would need to be added to the key as well.
362
- contents = babelDataCache.get(data);
363
- if (contents === undefined) {
364
- const transformedData = await transformWithBabel(request, data, pluginOptions);
365
- contents = Buffer.from(transformedData, 'utf-8');
366
- babelDataCache.set(data, contents);
367
- }
368
- (_f = pluginOptions.sourceFileCache) === null || _f === void 0 ? void 0 : _f.typeScriptFileCache.set((0, node_url_1.pathToFileURL)(request).href, contents);
286
+ contents = await javascriptTransformer.transformData(request, typescriptResult.content, true /* skipLinker */);
287
+ (_e = pluginOptions.sourceFileCache) === null || _e === void 0 ? void 0 : _e.typeScriptFileCache.set((0, node_url_1.pathToFileURL)(request).href, contents);
369
288
  }
370
289
  return {
371
290
  contents,
@@ -380,9 +299,7 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
380
299
  // would need to be added to the key as well as a check for any change of content.
381
300
  let contents = (_a = pluginOptions.sourceFileCache) === null || _a === void 0 ? void 0 : _a.babelFileCache.get(args.path);
382
301
  if (contents === undefined) {
383
- const data = await fs.readFile(args.path, 'utf-8');
384
- const transformedData = await transformWithBabel(args.path, data, pluginOptions);
385
- contents = Buffer.from(transformedData, 'utf-8');
302
+ contents = await javascriptTransformer.transformFile(args.path);
386
303
  (_b = pluginOptions.sourceFileCache) === null || _b === void 0 ? void 0 : _b.babelFileCache.set(args.path, contents);
387
304
  }
388
305
  return {
@@ -401,103 +318,6 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
401
318
  };
402
319
  }
403
320
  exports.createCompilerPlugin = createCompilerPlugin;
404
- function createFileEmitter(program, transformers = {}, onAfterEmit) {
405
- return async (file) => {
406
- const sourceFile = program.getSourceFile(file);
407
- if (!sourceFile) {
408
- return undefined;
409
- }
410
- let content;
411
- program.emit(sourceFile, (filename, data) => {
412
- if (/\.[cm]?js$/.test(filename)) {
413
- content = data;
414
- }
415
- }, undefined /* cancellationToken */, undefined /* emitOnlyDtsFiles */, transformers);
416
- onAfterEmit === null || onAfterEmit === void 0 ? void 0 : onAfterEmit(sourceFile);
417
- return { content, dependencies: [] };
418
- };
419
- }
420
- async function transformWithBabel(filename, data, pluginOptions) {
421
- var _a;
422
- const forceAsyncTransformation = !/[\\/][_f]?esm2015[\\/]/.test(filename) && /async\s+function\s*\*/.test(data);
423
- const shouldLink = await (0, webpack_loader_1.requiresLinking)(filename, data);
424
- const useInputSourcemap = pluginOptions.sourcemap &&
425
- (!!pluginOptions.thirdPartySourcemaps || !/[\\/]node_modules[\\/]/.test(filename));
426
- // If no additional transformations are needed, return the data directly
427
- if (!forceAsyncTransformation && !pluginOptions.advancedOptimizations && !shouldLink) {
428
- // Strip sourcemaps if they should not be used
429
- return useInputSourcemap ? data : data.replace(/^\/\/# sourceMappingURL=[^\r\n]*/gm, '');
430
- }
431
- const angularPackage = /[\\/]node_modules[\\/]@angular[\\/]/.test(filename);
432
- const linkerPluginCreator = shouldLink
433
- ? (await (0, load_esm_1.loadEsmModule)('@angular/compiler-cli/linker/babel')).createEs2015LinkerPlugin
434
- : undefined;
435
- const result = await (0, core_1.transformAsync)(data, {
436
- filename,
437
- inputSourceMap: (useInputSourcemap ? undefined : false),
438
- sourceMaps: pluginOptions.sourcemap ? 'inline' : false,
439
- compact: false,
440
- configFile: false,
441
- babelrc: false,
442
- browserslistConfigFile: false,
443
- plugins: [],
444
- presets: [
445
- [
446
- application_1.default,
447
- {
448
- angularLinker: {
449
- shouldLink,
450
- jitMode: false,
451
- linkerPluginCreator,
452
- },
453
- forceAsyncTransformation,
454
- optimize: pluginOptions.advancedOptimizations && {
455
- looseEnums: angularPackage,
456
- pureTopLevel: angularPackage,
457
- },
458
- },
459
- ],
460
- ],
461
- });
462
- return (_a = result === null || result === void 0 ? void 0 : result.code) !== null && _a !== void 0 ? _a : data;
463
- }
464
- function findAffectedFiles(builder, { ignoreForDiagnostics, ignoreForEmit, incrementalCompilation }) {
465
- const affectedFiles = new Set();
466
- // eslint-disable-next-line no-constant-condition
467
- while (true) {
468
- const result = builder.getSemanticDiagnosticsOfNextAffectedFile(undefined, (sourceFile) => {
469
- // If the affected file is a TTC shim, add the shim's original source file.
470
- // This ensures that changes that affect TTC are typechecked even when the changes
471
- // are otherwise unrelated from a TS perspective and do not result in Ivy codegen changes.
472
- // For example, changing @Input property types of a directive used in another component's
473
- // template.
474
- // A TTC shim is a file that has been ignored for diagnostics and has a filename ending in `.ngtypecheck.ts`.
475
- if (ignoreForDiagnostics.has(sourceFile) && sourceFile.fileName.endsWith('.ngtypecheck.ts')) {
476
- // This file name conversion relies on internal compiler logic and should be converted
477
- // to an official method when available. 15 is length of `.ngtypecheck.ts`
478
- const originalFilename = sourceFile.fileName.slice(0, -15) + '.ts';
479
- const originalSourceFile = builder.getSourceFile(originalFilename);
480
- if (originalSourceFile) {
481
- affectedFiles.add(originalSourceFile);
482
- }
483
- return true;
484
- }
485
- return false;
486
- });
487
- if (!result) {
488
- break;
489
- }
490
- affectedFiles.add(result.affected);
491
- }
492
- // A file is also affected if the Angular compiler requires it to be emitted
493
- for (const sourceFile of builder.getSourceFiles()) {
494
- if (ignoreForEmit.has(sourceFile) || incrementalCompilation.safeToSkipEmit(sourceFile)) {
495
- continue;
496
- }
497
- affectedFiles.add(sourceFile);
498
- }
499
- return affectedFiles;
500
- }
501
321
  function createMissingFileError(request, original, root) {
502
322
  const error = {
503
323
  text: `File '${path.relative(root, request)}' is missing from the TypeScript compilation.`,
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @license
3
+ * Copyright Google LLC All Rights Reserved.
4
+ *
5
+ * Use of this source code is governed by an MIT-style license that can be
6
+ * found in the LICENSE file at https://angular.io/license
7
+ */
8
+ interface JavaScriptTransformRequest {
9
+ filename: string;
10
+ data: string;
11
+ sourcemap: boolean;
12
+ thirdPartySourcemaps: boolean;
13
+ advancedOptimizations: boolean;
14
+ forceAsyncTransformation?: boolean;
15
+ skipLinker: boolean;
16
+ }
17
+ export default function transformJavaScript(request: JavaScriptTransformRequest): Promise<Uint8Array>;
18
+ export {};
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ /**
3
+ * @license
4
+ * Copyright Google LLC All Rights Reserved.
5
+ *
6
+ * Use of this source code is governed by an MIT-style license that can be
7
+ * found in the LICENSE file at https://angular.io/license
8
+ */
9
+ var __importDefault = (this && this.__importDefault) || function (mod) {
10
+ return (mod && mod.__esModule) ? mod : { "default": mod };
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ const core_1 = require("@babel/core");
14
+ const promises_1 = require("node:fs/promises");
15
+ const application_1 = __importDefault(require("../../babel/presets/application"));
16
+ const webpack_loader_1 = require("../../babel/webpack-loader");
17
+ const load_esm_1 = require("../../utils/load-esm");
18
+ async function transformJavaScript(request) {
19
+ var _a;
20
+ (_a = request.data) !== null && _a !== void 0 ? _a : (request.data = await (0, promises_1.readFile)(request.filename, 'utf-8'));
21
+ const transformedData = await transformWithBabel(request);
22
+ return Buffer.from(transformedData, 'utf-8');
23
+ }
24
+ exports.default = transformJavaScript;
25
+ let linkerPluginCreator;
26
+ async function transformWithBabel({ filename, data, ...options }) {
27
+ var _a, _b;
28
+ const forceAsyncTransformation = (_a = options.forceAsyncTransformation) !== null && _a !== void 0 ? _a : (!/[\\/][_f]?esm2015[\\/]/.test(filename) && /async\s+function\s*\*/.test(data));
29
+ const shouldLink = !options.skipLinker && (await (0, webpack_loader_1.requiresLinking)(filename, data));
30
+ const useInputSourcemap = options.sourcemap &&
31
+ (!!options.thirdPartySourcemaps || !/[\\/]node_modules[\\/]/.test(filename));
32
+ // If no additional transformations are needed, return the data directly
33
+ if (!forceAsyncTransformation && !options.advancedOptimizations && !shouldLink) {
34
+ // Strip sourcemaps if they should not be used
35
+ return useInputSourcemap ? data : data.replace(/^\/\/# sourceMappingURL=[^\r\n]*/gm, '');
36
+ }
37
+ const angularPackage = /[\\/]node_modules[\\/]@angular[\\/]/.test(filename);
38
+ // Lazy load the linker plugin only when linking is required
39
+ if (shouldLink) {
40
+ linkerPluginCreator !== null && linkerPluginCreator !== void 0 ? linkerPluginCreator : (linkerPluginCreator = (await (0, load_esm_1.loadEsmModule)('@angular/compiler-cli/linker/babel')).createEs2015LinkerPlugin);
41
+ }
42
+ const result = await (0, core_1.transformAsync)(data, {
43
+ filename,
44
+ inputSourceMap: (useInputSourcemap ? undefined : false),
45
+ sourceMaps: options.sourcemap ? 'inline' : false,
46
+ compact: false,
47
+ configFile: false,
48
+ babelrc: false,
49
+ browserslistConfigFile: false,
50
+ plugins: [],
51
+ presets: [
52
+ [
53
+ application_1.default,
54
+ {
55
+ angularLinker: linkerPluginCreator && {
56
+ shouldLink,
57
+ jitMode: false,
58
+ linkerPluginCreator,
59
+ },
60
+ forceAsyncTransformation,
61
+ optimize: options.advancedOptimizations && {
62
+ looseEnums: angularPackage,
63
+ pureTopLevel: angularPackage,
64
+ },
65
+ },
66
+ ],
67
+ ],
68
+ });
69
+ return (_b = result === null || result === void 0 ? void 0 : result.code) !== null && _b !== void 0 ? _b : data;
70
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * @license
3
+ * Copyright Google LLC All Rights Reserved.
4
+ *
5
+ * Use of this source code is governed by an MIT-style license that can be
6
+ * found in the LICENSE file at https://angular.io/license
7
+ */
8
+ /**
9
+ * Transformation options that should apply to all transformed files and data.
10
+ */
11
+ export interface JavaScriptTransformerOptions {
12
+ sourcemap: boolean;
13
+ thirdPartySourcemaps?: boolean;
14
+ advancedOptimizations?: boolean;
15
+ }
16
+ /**
17
+ * A class that performs transformation of JavaScript files and raw data.
18
+ * A worker pool is used to distribute the transformation actions and allow
19
+ * parallel processing. Transformation behavior is based on the filename and
20
+ * data. Transformations may include: async downleveling, Angular linking,
21
+ * and advanced optimizations.
22
+ */
23
+ export declare class JavaScriptTransformer {
24
+ #private;
25
+ private options;
26
+ constructor(options: JavaScriptTransformerOptions, maxThreads?: number);
27
+ /**
28
+ * Performs JavaScript transformations on a file from the filesystem.
29
+ * If no transformations are required, the data for the original file will be returned.
30
+ * @param filename The full path to the file.
31
+ * @returns A promise that resolves to a UTF-8 encoded Uint8Array containing the result.
32
+ */
33
+ transformFile(filename: string): Promise<Uint8Array>;
34
+ /**
35
+ * Performs JavaScript transformations on the provided data of a file. The file does not need
36
+ * to exist on the filesystem.
37
+ * @param filename The full path of the file represented by the data.
38
+ * @param data The data of the file that should be transformed.
39
+ * @param skipLinker If true, bypass all Angular linker processing; if false, attempt linking.
40
+ * @returns A promise that resolves to a UTF-8 encoded Uint8Array containing the result.
41
+ */
42
+ transformData(filename: string, data: string, skipLinker: boolean): Promise<Uint8Array>;
43
+ /**
44
+ * Stops all active transformation tasks and shuts down all workers.
45
+ * @returns A void promise that resolves when closing is complete.
46
+ */
47
+ close(): Promise<void>;
48
+ }
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ /**
3
+ * @license
4
+ * Copyright Google LLC All Rights Reserved.
5
+ *
6
+ * Use of this source code is governed by an MIT-style license that can be
7
+ * found in the LICENSE file at https://angular.io/license
8
+ */
9
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
10
+ if (kind === "m") throw new TypeError("Private method is not writable");
11
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
12
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
13
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
14
+ };
15
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
16
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
17
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
18
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
19
+ };
20
+ var __importDefault = (this && this.__importDefault) || function (mod) {
21
+ return (mod && mod.__esModule) ? mod : { "default": mod };
22
+ };
23
+ var _JavaScriptTransformer_workerPool;
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.JavaScriptTransformer = void 0;
26
+ const piscina_1 = __importDefault(require("piscina"));
27
+ /**
28
+ * A class that performs transformation of JavaScript files and raw data.
29
+ * A worker pool is used to distribute the transformation actions and allow
30
+ * parallel processing. Transformation behavior is based on the filename and
31
+ * data. Transformations may include: async downleveling, Angular linking,
32
+ * and advanced optimizations.
33
+ */
34
+ class JavaScriptTransformer {
35
+ constructor(options, maxThreads) {
36
+ this.options = options;
37
+ _JavaScriptTransformer_workerPool.set(this, void 0);
38
+ __classPrivateFieldSet(this, _JavaScriptTransformer_workerPool, new piscina_1.default({
39
+ filename: require.resolve('./javascript-transformer-worker'),
40
+ maxThreads,
41
+ }), "f");
42
+ }
43
+ /**
44
+ * Performs JavaScript transformations on a file from the filesystem.
45
+ * If no transformations are required, the data for the original file will be returned.
46
+ * @param filename The full path to the file.
47
+ * @returns A promise that resolves to a UTF-8 encoded Uint8Array containing the result.
48
+ */
49
+ transformFile(filename) {
50
+ // Always send the request to a worker. Files are almost always from node modules which measn
51
+ // they may need linking. The data is also not yet available to perform most transformation checks.
52
+ return __classPrivateFieldGet(this, _JavaScriptTransformer_workerPool, "f").run({
53
+ filename,
54
+ ...this.options,
55
+ });
56
+ }
57
+ /**
58
+ * Performs JavaScript transformations on the provided data of a file. The file does not need
59
+ * to exist on the filesystem.
60
+ * @param filename The full path of the file represented by the data.
61
+ * @param data The data of the file that should be transformed.
62
+ * @param skipLinker If true, bypass all Angular linker processing; if false, attempt linking.
63
+ * @returns A promise that resolves to a UTF-8 encoded Uint8Array containing the result.
64
+ */
65
+ async transformData(filename, data, skipLinker) {
66
+ // Perform a quick test to determine if the data needs any transformations.
67
+ // This allows directly returning the data without the worker communication overhead.
68
+ let forceAsyncTransformation;
69
+ if (skipLinker && !this.options.advancedOptimizations) {
70
+ // If the linker is being skipped and no optimizations are needed, only async transformation is left.
71
+ // This checks for async generator functions. All other async transformation is handled by esbuild.
72
+ forceAsyncTransformation = data.includes('async') && /async\s+function\s*\*/.test(data);
73
+ if (!forceAsyncTransformation) {
74
+ return Buffer.from(data, 'utf-8');
75
+ }
76
+ }
77
+ return __classPrivateFieldGet(this, _JavaScriptTransformer_workerPool, "f").run({
78
+ filename,
79
+ data,
80
+ // Send the async check result if present to avoid rechecking in the worker
81
+ forceAsyncTransformation,
82
+ skipLinker,
83
+ ...this.options,
84
+ });
85
+ }
86
+ /**
87
+ * Stops all active transformation tasks and shuts down all workers.
88
+ * @returns A void promise that resolves when closing is complete.
89
+ */
90
+ close() {
91
+ return __classPrivateFieldGet(this, _JavaScriptTransformer_workerPool, "f").destroy();
92
+ }
93
+ }
94
+ exports.JavaScriptTransformer = JavaScriptTransformer;
95
+ _JavaScriptTransformer_workerPool = new WeakMap();