@atlaspack/transformer-js 2.12.1-canary.3354

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.
@@ -0,0 +1,1071 @@
1
+ // @flow
2
+ import type {
3
+ JSONObject,
4
+ EnvMap,
5
+ SourceLocation,
6
+ FilePath,
7
+ FileCreateInvalidation,
8
+ } from '@atlaspack/types';
9
+ import type {SchemaEntity} from '@atlaspack/utils';
10
+ import type {Diagnostic} from '@atlaspack/diagnostic';
11
+ import SourceMap from '@parcel/source-map';
12
+ import {Transformer} from '@atlaspack/plugin';
13
+ import {transform, transformAsync} from '@atlaspack/rust';
14
+ import browserslist from 'browserslist';
15
+ import semver from 'semver';
16
+ import nullthrows from 'nullthrows';
17
+ import ThrowableDiagnostic, {
18
+ encodeJSONKeyComponent,
19
+ convertSourceLocationToHighlight,
20
+ } from '@atlaspack/diagnostic';
21
+ import {validateSchema, remapSourceLocation, globMatch} from '@atlaspack/utils';
22
+ import pkg from '../package.json';
23
+
24
+ const JSX_EXTENSIONS = {
25
+ jsx: true,
26
+ tsx: true,
27
+ };
28
+
29
+ const JSX_PRAGMA = {
30
+ react: {
31
+ pragma: 'React.createElement',
32
+ pragmaFrag: 'React.Fragment',
33
+ automatic: '>= 17.0.0 || ^16.14.0 || >= 0.0.0-0 < 0.0.0',
34
+ },
35
+ preact: {
36
+ pragma: 'h',
37
+ pragmaFrag: 'Fragment',
38
+ automatic: '>= 10.5.0',
39
+ },
40
+ nervjs: {
41
+ pragma: 'Nerv.createElement',
42
+ pragmaFrag: undefined,
43
+ automatic: undefined,
44
+ },
45
+ hyperapp: {
46
+ pragma: 'h',
47
+ pragmaFrag: undefined,
48
+ automatic: undefined,
49
+ },
50
+ };
51
+
52
+ const BROWSER_MAPPING = {
53
+ and_chr: 'chrome',
54
+ and_ff: 'firefox',
55
+ ie_mob: 'ie',
56
+ ios_saf: 'ios',
57
+ op_mob: 'opera',
58
+ and_qq: null,
59
+ and_uc: null,
60
+ baidu: null,
61
+ bb: null,
62
+ kaios: null,
63
+ op_mini: null,
64
+ };
65
+
66
+ // List of browsers to exclude when the esmodule target is specified.
67
+ // Based on https://caniuse.com/#feat=es6-module
68
+ const ESMODULE_BROWSERS = [
69
+ 'not ie <= 11',
70
+ 'not edge < 16',
71
+ 'not firefox < 60',
72
+ 'not chrome < 61',
73
+ 'not safari < 11',
74
+ 'not opera < 48',
75
+ 'not ios_saf < 11',
76
+ 'not op_mini all',
77
+ 'not android < 76',
78
+ 'not blackberry > 0',
79
+ 'not op_mob > 0',
80
+ 'not and_chr < 76',
81
+ 'not and_ff < 68',
82
+ 'not ie_mob > 0',
83
+ 'not and_uc > 0',
84
+ 'not samsung < 8.2',
85
+ 'not and_qq > 0',
86
+ 'not baidu > 0',
87
+ 'not kaios > 0',
88
+ ];
89
+
90
+ const CONFIG_SCHEMA: SchemaEntity = {
91
+ type: 'object',
92
+ properties: {
93
+ inlineFS: {
94
+ type: 'boolean',
95
+ },
96
+ inlineEnvironment: {
97
+ oneOf: [
98
+ {
99
+ type: 'boolean',
100
+ },
101
+ {
102
+ type: 'array',
103
+ items: {
104
+ type: 'string',
105
+ },
106
+ },
107
+ ],
108
+ },
109
+ unstable_inlineConstants: {
110
+ type: 'boolean',
111
+ },
112
+ },
113
+ additionalProperties: false,
114
+ };
115
+
116
+ const SCRIPT_ERRORS = {
117
+ browser: {
118
+ message: 'Browser scripts cannot have imports or exports.',
119
+ hint: 'Add the type="module" attribute to the <script> tag.',
120
+ },
121
+ 'web-worker': {
122
+ message:
123
+ 'Web workers cannot have imports or exports without the `type: "module"` option.',
124
+ hint: "Add {type: 'module'} as a second argument to the Worker constructor.",
125
+ },
126
+ 'service-worker': {
127
+ message:
128
+ 'Service workers cannot have imports or exports without the `type: "module"` option.',
129
+ hint: "Add {type: 'module'} as a second argument to the navigator.serviceWorker.register() call.",
130
+ },
131
+ };
132
+
133
+ type TSConfig = {
134
+ compilerOptions?: {
135
+ // https://www.typescriptlang.org/tsconfig#jsx
136
+ jsx?: 'react' | 'react-jsx' | 'react-jsxdev' | 'preserve' | 'react-native',
137
+ // https://www.typescriptlang.org/tsconfig#jsxFactory
138
+ jsxFactory?: string,
139
+ // https://www.typescriptlang.org/tsconfig#jsxFragmentFactory
140
+ jsxFragmentFactory?: string,
141
+ // https://www.typescriptlang.org/tsconfig#jsxImportSource
142
+ jsxImportSource?: string,
143
+ // https://www.typescriptlang.org/tsconfig#experimentalDecorators
144
+ experimentalDecorators?: boolean,
145
+ // https://www.typescriptlang.org/tsconfig#useDefineForClassFields
146
+ useDefineForClassFields?: boolean,
147
+ // https://www.typescriptlang.org/tsconfig#target
148
+ target?: string, // 'es3' | 'es5' | 'es6' | 'es2015' | ... |'es2022' | ... | 'esnext'
149
+ ...
150
+ },
151
+ ...
152
+ };
153
+
154
+ type MacroAsset = {|
155
+ type: string,
156
+ content: string,
157
+ |};
158
+
159
+ // NOTE: Make sure this is in sync with the TypeScript definition in the @atlaspack/macros package.
160
+ type MacroContext = {|
161
+ addAsset(asset: MacroAsset): void,
162
+ invalidateOnFileChange(FilePath): void,
163
+ invalidateOnFileCreate(FileCreateInvalidation): void,
164
+ invalidateOnEnvChange(string): void,
165
+ invalidateOnStartup(): void,
166
+ invalidateOnBuild(): void,
167
+ |};
168
+
169
+ export default (new Transformer({
170
+ async loadConfig({config, options}) {
171
+ let pkg = await config.getPackage();
172
+ let isJSX,
173
+ pragma,
174
+ pragmaFrag,
175
+ jsxImportSource,
176
+ automaticJSXRuntime,
177
+ reactRefresh,
178
+ decorators,
179
+ useDefineForClassFields;
180
+ if (config.isSource) {
181
+ let reactLib;
182
+ if (pkg?.alias && pkg.alias['react']) {
183
+ // e.g.: `{ alias: { "react": "preact/compat" } }`
184
+ reactLib = 'react';
185
+ } else {
186
+ // Find a dependency that we can map to a JSX pragma
187
+ reactLib = Object.keys(JSX_PRAGMA).find(
188
+ libName =>
189
+ pkg?.dependencies?.[libName] ||
190
+ pkg?.devDependencies?.[libName] ||
191
+ pkg?.peerDependencies?.[libName],
192
+ );
193
+ }
194
+
195
+ reactRefresh =
196
+ options.hmrOptions &&
197
+ options.mode === 'development' &&
198
+ Boolean(
199
+ pkg?.dependencies?.react ||
200
+ pkg?.devDependencies?.react ||
201
+ pkg?.peerDependencies?.react,
202
+ );
203
+
204
+ let tsconfig = await config.getConfigFrom<TSConfig>(
205
+ options.projectRoot + '/index',
206
+ ['tsconfig.json', 'jsconfig.json'],
207
+ );
208
+ let compilerOptions = tsconfig?.contents?.compilerOptions;
209
+
210
+ // Use explicitly defined JSX options in tsconfig.json over inferred values from dependencies.
211
+ pragma =
212
+ compilerOptions?.jsxFactory ||
213
+ (reactLib ? JSX_PRAGMA[reactLib].pragma : undefined);
214
+ pragmaFrag =
215
+ compilerOptions?.jsxFragmentFactory ||
216
+ (reactLib ? JSX_PRAGMA[reactLib].pragmaFrag : undefined);
217
+
218
+ if (
219
+ compilerOptions?.jsx === 'react-jsx' ||
220
+ compilerOptions?.jsx === 'react-jsxdev' ||
221
+ compilerOptions?.jsxImportSource
222
+ ) {
223
+ jsxImportSource = compilerOptions?.jsxImportSource;
224
+ automaticJSXRuntime = true;
225
+ } else if (reactLib) {
226
+ let effectiveReactLib =
227
+ pkg?.alias && pkg.alias['react'] === 'preact/compat'
228
+ ? 'preact'
229
+ : reactLib;
230
+ let automaticVersion = JSX_PRAGMA[effectiveReactLib]?.automatic;
231
+ let reactLibVersion =
232
+ pkg?.dependencies?.[effectiveReactLib] ||
233
+ pkg?.devDependencies?.[effectiveReactLib] ||
234
+ pkg?.peerDependencies?.[effectiveReactLib];
235
+ reactLibVersion = reactLibVersion
236
+ ? semver.validRange(reactLibVersion)
237
+ : null;
238
+ let minReactLibVersion =
239
+ reactLibVersion !== null && reactLibVersion !== '*'
240
+ ? semver.minVersion(reactLibVersion)?.toString()
241
+ : null;
242
+
243
+ automaticJSXRuntime =
244
+ automaticVersion &&
245
+ !compilerOptions?.jsxFactory &&
246
+ minReactLibVersion != null &&
247
+ semver.satisfies(minReactLibVersion, automaticVersion, {
248
+ includePrerelease: true,
249
+ });
250
+
251
+ if (automaticJSXRuntime) {
252
+ jsxImportSource = reactLib;
253
+ }
254
+ }
255
+
256
+ isJSX = Boolean(compilerOptions?.jsx || pragma);
257
+ decorators = compilerOptions?.experimentalDecorators;
258
+ useDefineForClassFields = compilerOptions?.useDefineForClassFields;
259
+ if (
260
+ useDefineForClassFields === undefined &&
261
+ compilerOptions?.target != null
262
+ ) {
263
+ // Default useDefineForClassFields to true if target is ES2022 or higher (including ESNext)
264
+ let target = compilerOptions.target.slice(2);
265
+ if (target === 'next') {
266
+ useDefineForClassFields = true;
267
+ } else {
268
+ useDefineForClassFields = Number(target) >= 2022;
269
+ }
270
+ }
271
+ }
272
+
273
+ // Check if we should ignore fs calls
274
+ // See https://github.com/defunctzombie/node-browser-resolve#skip
275
+ let ignoreFS =
276
+ pkg &&
277
+ pkg.browser &&
278
+ typeof pkg.browser === 'object' &&
279
+ pkg.browser.fs === false;
280
+
281
+ let conf = await config.getConfigFrom(options.projectRoot + '/index', [], {
282
+ packageKey: '@atlaspack/transformer-js',
283
+ });
284
+
285
+ let inlineEnvironment = config.isSource;
286
+ let inlineFS = !ignoreFS;
287
+ let inlineConstants = false;
288
+ if (conf && conf.contents) {
289
+ validateSchema.diagnostic(
290
+ CONFIG_SCHEMA,
291
+ {
292
+ data: conf.contents,
293
+ // FIXME
294
+ source: await options.inputFS.readFile(conf.filePath, 'utf8'),
295
+ filePath: conf.filePath,
296
+ prependKey: `/${encodeJSONKeyComponent('@atlaspack/transformer-js')}`,
297
+ },
298
+ // FIXME
299
+ '@atlaspack/transformer-js',
300
+ 'Invalid config for @atlaspack/transformer-js',
301
+ );
302
+
303
+ inlineEnvironment = conf.contents?.inlineEnvironment ?? inlineEnvironment;
304
+ inlineFS = conf.contents?.inlineFS ?? inlineFS;
305
+ inlineConstants =
306
+ conf.contents?.unstable_inlineConstants ?? inlineConstants;
307
+ }
308
+
309
+ return {
310
+ isJSX,
311
+ automaticJSXRuntime,
312
+ jsxImportSource,
313
+ pragma,
314
+ pragmaFrag,
315
+ inlineEnvironment,
316
+ inlineFS,
317
+ inlineConstants,
318
+ reactRefresh,
319
+ decorators,
320
+ useDefineForClassFields,
321
+ };
322
+ },
323
+ async transform({asset, config, options, logger}) {
324
+ let [code, originalMap] = await Promise.all([
325
+ asset.getBuffer(),
326
+ asset.getMap(),
327
+ ]);
328
+
329
+ let targets;
330
+ if (asset.env.isElectron() && asset.env.engines.electron) {
331
+ targets = {
332
+ electron: semver.minVersion(asset.env.engines.electron)?.toString(),
333
+ };
334
+ } else if (asset.env.isBrowser() && asset.env.engines.browsers) {
335
+ targets = {};
336
+
337
+ let browsers = Array.isArray(asset.env.engines.browsers)
338
+ ? asset.env.engines.browsers
339
+ : [asset.env.engines.browsers];
340
+
341
+ // If the output format is esmodule, exclude browsers
342
+ // that support them natively so that we transpile less.
343
+ if (asset.env.outputFormat === 'esmodule') {
344
+ browsers = [...browsers, ...ESMODULE_BROWSERS];
345
+ }
346
+
347
+ browsers = browserslist(browsers);
348
+ for (let browser of browsers) {
349
+ let [name, version] = browser.split(' ');
350
+ if (BROWSER_MAPPING.hasOwnProperty(name)) {
351
+ name = BROWSER_MAPPING[name];
352
+ if (!name) {
353
+ continue;
354
+ }
355
+ }
356
+
357
+ let [major, minor = '0', patch = '0'] = version
358
+ .split('-')[0]
359
+ .split('.');
360
+ if (isNaN(major) || isNaN(minor) || isNaN(patch)) {
361
+ continue;
362
+ }
363
+ let semverVersion = `${major}.${minor}.${patch}`;
364
+
365
+ if (targets[name] == null || semver.gt(targets[name], semverVersion)) {
366
+ targets[name] = semverVersion;
367
+ }
368
+ }
369
+ } else if (asset.env.isNode() && asset.env.engines.node) {
370
+ targets = {node: semver.minVersion(asset.env.engines.node)?.toString()};
371
+ }
372
+
373
+ let env: EnvMap = {};
374
+
375
+ if (!config?.inlineEnvironment) {
376
+ if (options.env.NODE_ENV != null) {
377
+ env.NODE_ENV = options.env.NODE_ENV;
378
+ }
379
+
380
+ if (process.env.ATLASPACK_BUILD_ENV === 'test') {
381
+ env.ATLASPACK_BUILD_ENV = 'test';
382
+ }
383
+ } else if (Array.isArray(config?.inlineEnvironment)) {
384
+ for (let match of globMatch(
385
+ Object.keys(options.env),
386
+ config.inlineEnvironment,
387
+ )) {
388
+ env[match] = String(options.env[match]);
389
+ }
390
+ } else {
391
+ for (let key in options.env) {
392
+ if (!key.startsWith('npm_')) {
393
+ env[key] = String(options.env[key]);
394
+ }
395
+ }
396
+ }
397
+
398
+ let supportsModuleWorkers =
399
+ asset.env.shouldScopeHoist && asset.env.supports('worker-module', true);
400
+ let isJSX = Boolean(config?.isJSX);
401
+ if (asset.isSource) {
402
+ if (asset.type === 'ts') {
403
+ isJSX = false;
404
+ } else if (!isJSX) {
405
+ isJSX = Boolean(JSX_EXTENSIONS[asset.type]);
406
+ }
407
+ }
408
+
409
+ let macroAssets = [];
410
+ let {
411
+ dependencies,
412
+ code: compiledCode,
413
+ map,
414
+ shebang,
415
+ hoist_result,
416
+ symbol_result,
417
+ needs_esm_helpers,
418
+ diagnostics,
419
+ used_env,
420
+ has_node_replacements,
421
+ is_constant_module,
422
+ } = await (transformAsync || transform)({
423
+ filename: asset.filePath,
424
+ code,
425
+ module_id: asset.id,
426
+ project_root: options.projectRoot,
427
+ replace_env: !asset.env.isNode(),
428
+ inline_fs: Boolean(config?.inlineFS) && !asset.env.isNode(),
429
+ insert_node_globals:
430
+ !asset.env.isNode() && asset.env.sourceType !== 'script',
431
+ node_replacer: asset.env.isNode(),
432
+ is_browser: asset.env.isBrowser(),
433
+ is_worker: asset.env.isWorker(),
434
+ env,
435
+ is_type_script: asset.type === 'ts' || asset.type === 'tsx',
436
+ is_jsx: isJSX,
437
+ jsx_pragma: config?.pragma,
438
+ jsx_pragma_frag: config?.pragmaFrag,
439
+ automatic_jsx_runtime: Boolean(config?.automaticJSXRuntime),
440
+ jsx_import_source: config?.jsxImportSource,
441
+ is_development: options.mode === 'development',
442
+ react_refresh:
443
+ asset.env.isBrowser() &&
444
+ !asset.env.isLibrary &&
445
+ !asset.env.isWorker() &&
446
+ !asset.env.isWorklet() &&
447
+ Boolean(config?.reactRefresh),
448
+ decorators: Boolean(config?.decorators),
449
+ use_define_for_class_fields: Boolean(config?.useDefineForClassFields),
450
+ targets,
451
+ source_maps: !!asset.env.sourceMap,
452
+ scope_hoist:
453
+ asset.env.shouldScopeHoist && asset.env.sourceType !== 'script',
454
+ source_type: asset.env.sourceType === 'script' ? 'Script' : 'Module',
455
+ supports_module_workers: supportsModuleWorkers,
456
+ is_library: asset.env.isLibrary,
457
+ is_esm_output: asset.env.outputFormat === 'esmodule',
458
+ trace_bailouts: options.logLevel === 'verbose',
459
+ is_swc_helpers: /@swc[/\\]helpers/.test(asset.filePath),
460
+ standalone: asset.query.has('standalone'),
461
+ inline_constants: config.inlineConstants,
462
+ callMacro: asset.isSource
463
+ ? async (err, src, exportName, args, loc) => {
464
+ let mod;
465
+ try {
466
+ mod = await options.packageManager.require(src, asset.filePath);
467
+
468
+ // Default interop for CommonJS modules.
469
+ if (
470
+ exportName === 'default' &&
471
+ !mod.__esModule &&
472
+ // $FlowFixMe
473
+ Object.prototype.toString.call(config) !== '[object Module]'
474
+ ) {
475
+ mod = {default: mod};
476
+ }
477
+
478
+ if (!Object.hasOwnProperty.call(mod, exportName)) {
479
+ throw new Error(`"${src}" does not export "${exportName}".`);
480
+ }
481
+ } catch (err) {
482
+ throw {
483
+ kind: 1,
484
+ message: err.message,
485
+ };
486
+ }
487
+
488
+ try {
489
+ if (typeof mod[exportName] === 'function') {
490
+ let ctx: MacroContext = {
491
+ // Allows macros to emit additional assets to add as dependencies (e.g. css).
492
+ addAsset(a: MacroAsset) {
493
+ let k = String(macroAssets.length);
494
+ let map;
495
+ if (asset.env.sourceMap) {
496
+ // Generate a source map that maps each line of the asset to the original macro call.
497
+ map = new SourceMap(options.projectRoot);
498
+ let mappings = [];
499
+ let line = 1;
500
+ for (let i = 0; i <= a.content.length; i++) {
501
+ if (i === a.content.length || a.content[i] === '\n') {
502
+ mappings.push({
503
+ generated: {
504
+ line,
505
+ column: 0,
506
+ },
507
+ source: asset.filePath,
508
+ original: {
509
+ line: loc.line,
510
+ column: loc.col,
511
+ },
512
+ });
513
+ line++;
514
+ }
515
+ }
516
+
517
+ map.addIndexedMappings(mappings);
518
+ if (originalMap) {
519
+ map.extends(originalMap);
520
+ } else {
521
+ map.setSourceContent(asset.filePath, code.toString());
522
+ }
523
+ }
524
+
525
+ macroAssets.push({
526
+ type: a.type,
527
+ content: a.content,
528
+ map,
529
+ uniqueKey: k,
530
+ });
531
+
532
+ asset.addDependency({
533
+ specifier: k,
534
+ specifierType: 'esm',
535
+ });
536
+ },
537
+ invalidateOnFileChange(filePath) {
538
+ asset.invalidateOnFileChange(filePath);
539
+ },
540
+ invalidateOnFileCreate(invalidation) {
541
+ asset.invalidateOnFileCreate(invalidation);
542
+ },
543
+ invalidateOnEnvChange(env) {
544
+ asset.invalidateOnEnvChange(env);
545
+ },
546
+ invalidateOnStartup() {
547
+ asset.invalidateOnStartup();
548
+ },
549
+ invalidateOnBuild() {
550
+ asset.invalidateOnBuild();
551
+ },
552
+ };
553
+
554
+ return mod[exportName].apply(ctx, args);
555
+ } else {
556
+ throw new Error(
557
+ `"${exportName}" in "${src}" is not a function.`,
558
+ );
559
+ }
560
+ } catch (err) {
561
+ // Remove atlaspack core from stack and build string so Rust can process errors more easily.
562
+ let stack = (err.stack || '').split('\n').slice(1);
563
+ let message = err.message;
564
+ for (let line of stack) {
565
+ if (line.includes(__filename)) {
566
+ break;
567
+ }
568
+ message += '\n' + line;
569
+ }
570
+ throw {
571
+ kind: 2,
572
+ message,
573
+ };
574
+ }
575
+ }
576
+ : null,
577
+ });
578
+
579
+ if (is_constant_module) {
580
+ asset.meta.isConstantModule = true;
581
+ }
582
+
583
+ let convertLoc = (loc): SourceLocation => {
584
+ let location = {
585
+ filePath: asset.filePath,
586
+ start: {
587
+ line: loc.start_line + Number(asset.meta.startLine ?? 1) - 1,
588
+ column: loc.start_col,
589
+ },
590
+ end: {
591
+ line: loc.end_line + Number(asset.meta.startLine ?? 1) - 1,
592
+ column: loc.end_col,
593
+ },
594
+ };
595
+
596
+ // If there is an original source map, use it to remap to the original source location.
597
+ if (originalMap) {
598
+ location = remapSourceLocation(location, originalMap);
599
+ }
600
+
601
+ return location;
602
+ };
603
+
604
+ if (diagnostics) {
605
+ let errors = diagnostics.filter(
606
+ d =>
607
+ d.severity === 'Error' ||
608
+ (d.severity === 'SourceError' && asset.isSource),
609
+ );
610
+ let warnings = diagnostics.filter(
611
+ d =>
612
+ d.severity === 'Warning' ||
613
+ (d.severity === 'SourceError' && !asset.isSource),
614
+ );
615
+ let convertDiagnostic = diagnostic => {
616
+ let message = diagnostic.message;
617
+ if (message === 'SCRIPT_ERROR') {
618
+ let err = SCRIPT_ERRORS[(asset.env.context: string)];
619
+ message = err?.message || SCRIPT_ERRORS.browser.message;
620
+ }
621
+
622
+ let res: Diagnostic = {
623
+ message,
624
+ codeFrames: [
625
+ {
626
+ filePath: asset.filePath,
627
+ codeHighlights: diagnostic.code_highlights?.map(highlight =>
628
+ convertSourceLocationToHighlight(
629
+ convertLoc(highlight.loc),
630
+ highlight.message ?? undefined,
631
+ ),
632
+ ),
633
+ },
634
+ ],
635
+ hints: diagnostic.hints,
636
+ };
637
+
638
+ if (diagnostic.documentation_url) {
639
+ res.documentationURL = diagnostic.documentation_url;
640
+ }
641
+
642
+ if (diagnostic.show_environment) {
643
+ if (asset.env.loc && asset.env.loc.filePath !== asset.filePath) {
644
+ res.codeFrames?.push({
645
+ filePath: asset.env.loc.filePath,
646
+ codeHighlights: [
647
+ convertSourceLocationToHighlight(
648
+ asset.env.loc,
649
+ 'The environment was originally created here',
650
+ ),
651
+ ],
652
+ });
653
+ }
654
+
655
+ let err = SCRIPT_ERRORS[(asset.env.context: string)];
656
+ if (err) {
657
+ if (!res.hints) {
658
+ res.hints = [err.hint];
659
+ } else {
660
+ res.hints.push(err.hint);
661
+ }
662
+ }
663
+ }
664
+
665
+ return res;
666
+ };
667
+
668
+ if (errors.length > 0) {
669
+ throw new ThrowableDiagnostic({
670
+ diagnostic: errors.map(convertDiagnostic),
671
+ });
672
+ }
673
+
674
+ logger.warn(warnings.map(convertDiagnostic));
675
+ }
676
+
677
+ if (shebang) {
678
+ asset.meta.interpreter = shebang;
679
+ }
680
+
681
+ if (has_node_replacements) {
682
+ asset.meta.has_node_replacements = has_node_replacements;
683
+ }
684
+
685
+ for (let env of used_env) {
686
+ asset.invalidateOnEnvChange(env);
687
+ }
688
+
689
+ for (let dep of dependencies) {
690
+ if (dep.kind === 'WebWorker') {
691
+ // Use native ES module output if the worker was created with `type: 'module'` and all targets
692
+ // support native module workers. Only do this if parent asset output format is also esmodule so that
693
+ // assets can be shared between workers and the main thread in the global output format.
694
+ let outputFormat;
695
+ if (
696
+ asset.env.outputFormat === 'esmodule' &&
697
+ dep.source_type === 'Module' &&
698
+ supportsModuleWorkers
699
+ ) {
700
+ outputFormat = 'esmodule';
701
+ } else {
702
+ outputFormat =
703
+ asset.env.outputFormat === 'commonjs' ? 'commonjs' : 'global';
704
+ }
705
+
706
+ let loc = convertLoc(dep.loc);
707
+ asset.addURLDependency(dep.specifier, {
708
+ loc,
709
+ env: {
710
+ context: 'web-worker',
711
+ sourceType: dep.source_type === 'Module' ? 'module' : 'script',
712
+ outputFormat,
713
+ loc,
714
+ },
715
+ meta: {
716
+ webworker: true,
717
+ placeholder: dep.placeholder,
718
+ },
719
+ });
720
+ } else if (dep.kind === 'ServiceWorker') {
721
+ let loc = convertLoc(dep.loc);
722
+ asset.addURLDependency(dep.specifier, {
723
+ loc,
724
+ needsStableName: true,
725
+ env: {
726
+ context: 'service-worker',
727
+ sourceType: dep.source_type === 'Module' ? 'module' : 'script',
728
+ outputFormat: 'global', // TODO: module service worker support
729
+ loc,
730
+ },
731
+ meta: {
732
+ placeholder: dep.placeholder,
733
+ },
734
+ });
735
+ } else if (dep.kind === 'Worklet') {
736
+ let loc = convertLoc(dep.loc);
737
+ asset.addURLDependency(dep.specifier, {
738
+ loc,
739
+ env: {
740
+ context: 'worklet',
741
+ sourceType: 'module',
742
+ outputFormat: 'esmodule', // Worklets require ESM
743
+ loc,
744
+ },
745
+ meta: {
746
+ placeholder: dep.placeholder,
747
+ },
748
+ });
749
+ } else if (dep.kind === 'Url') {
750
+ asset.addURLDependency(dep.specifier, {
751
+ bundleBehavior: 'isolated',
752
+ loc: convertLoc(dep.loc),
753
+ meta: {
754
+ placeholder: dep.placeholder,
755
+ },
756
+ });
757
+ } else if (dep.kind === 'File') {
758
+ asset.invalidateOnFileChange(dep.specifier);
759
+ } else {
760
+ let meta: JSONObject = {kind: dep.kind};
761
+ if (dep.attributes) {
762
+ meta.importAttributes = dep.attributes;
763
+ }
764
+
765
+ if (dep.placeholder) {
766
+ meta.placeholder = dep.placeholder;
767
+ }
768
+
769
+ let env;
770
+ if (dep.kind === 'DynamicImport') {
771
+ // https://html.spec.whatwg.org/multipage/webappapis.html#hostimportmoduledynamically(referencingscriptormodule,-modulerequest,-promisecapability)
772
+ if (asset.env.isWorklet() || asset.env.context === 'service-worker') {
773
+ let loc = convertLoc(dep.loc);
774
+ let diagnostic = {
775
+ message: `import() is not allowed in ${
776
+ asset.env.isWorklet() ? 'worklets' : 'service workers'
777
+ }.`,
778
+ codeFrames: [
779
+ {
780
+ filePath: asset.filePath,
781
+ codeHighlights: [convertSourceLocationToHighlight(loc)],
782
+ },
783
+ ],
784
+ hints: ['Try using a static `import`.'],
785
+ };
786
+
787
+ if (asset.env.loc) {
788
+ diagnostic.codeFrames.push({
789
+ filePath: asset.env.loc.filePath,
790
+ codeHighlights: [
791
+ convertSourceLocationToHighlight(
792
+ asset.env.loc,
793
+ 'The environment was originally created here',
794
+ ),
795
+ ],
796
+ });
797
+ }
798
+
799
+ throw new ThrowableDiagnostic({
800
+ diagnostic,
801
+ });
802
+ }
803
+
804
+ // If all of the target engines support dynamic import natively,
805
+ // we can output native ESM if scope hoisting is enabled.
806
+ // Only do this for scripts, rather than modules in the global
807
+ // output format so that assets can be shared between the bundles.
808
+ let outputFormat = asset.env.outputFormat;
809
+ if (
810
+ asset.env.sourceType === 'script' &&
811
+ asset.env.shouldScopeHoist &&
812
+ asset.env.supports('dynamic-import', true)
813
+ ) {
814
+ outputFormat = 'esmodule';
815
+ }
816
+
817
+ env = {
818
+ sourceType: 'module',
819
+ outputFormat,
820
+ loc: convertLoc(dep.loc),
821
+ };
822
+ }
823
+
824
+ // Always bundle helpers, even with includeNodeModules: false, except if this is a library.
825
+ let isHelper =
826
+ dep.is_helper &&
827
+ !(
828
+ dep.specifier.endsWith('/jsx-runtime') ||
829
+ dep.specifier.endsWith('/jsx-dev-runtime')
830
+ );
831
+ if (isHelper && !asset.env.isLibrary) {
832
+ env = {
833
+ ...env,
834
+ includeNodeModules: true,
835
+ };
836
+ }
837
+
838
+ // Add required version range for helpers.
839
+ let range;
840
+ if (isHelper) {
841
+ let idx = dep.specifier.indexOf('/');
842
+ if (dep.specifier[0] === '@') {
843
+ idx = dep.specifier.indexOf('/', idx + 1);
844
+ }
845
+ let module = idx >= 0 ? dep.specifier.slice(0, idx) : dep.specifier;
846
+ range = pkg.dependencies[module];
847
+ }
848
+
849
+ asset.addDependency({
850
+ specifier: dep.specifier,
851
+ specifierType: dep.kind === 'Require' ? 'commonjs' : 'esm',
852
+ loc: convertLoc(dep.loc),
853
+ priority: dep.kind === 'DynamicImport' ? 'lazy' : 'sync',
854
+ isOptional: dep.is_optional,
855
+ meta,
856
+ resolveFrom: isHelper ? __filename : undefined,
857
+ range,
858
+ env,
859
+ });
860
+ }
861
+ }
862
+
863
+ asset.meta.id = asset.id;
864
+ if (hoist_result) {
865
+ asset.symbols.ensure();
866
+ for (let {
867
+ exported,
868
+ local,
869
+ loc,
870
+ is_esm,
871
+ } of hoist_result.exported_symbols) {
872
+ asset.symbols.set(exported, local, convertLoc(loc), {isEsm: is_esm});
873
+ }
874
+
875
+ // deps is a map of dependencies that are keyed by placeholder or specifier
876
+ // If a placeholder is present, that is used first since placeholders are
877
+ // hashed with DependencyKind's.
878
+ // If not, the specifier is used along with its specifierType appended to
879
+ // it to separate dependencies with the same specifier.
880
+ let deps = new Map(
881
+ asset
882
+ .getDependencies()
883
+ .map(dep => [dep.meta.placeholder ?? dep.specifier, dep]),
884
+ );
885
+ for (let dep of deps.values()) {
886
+ dep.symbols.ensure();
887
+ }
888
+
889
+ for (let {
890
+ source,
891
+ local,
892
+ imported,
893
+ loc,
894
+ } of hoist_result.imported_symbols) {
895
+ let dep = deps.get(source);
896
+ if (!dep) continue;
897
+ dep.symbols.set(imported, local, convertLoc(loc));
898
+ }
899
+
900
+ for (let {source, local, imported, loc} of hoist_result.re_exports) {
901
+ let dep = deps.get(source);
902
+ if (!dep) continue;
903
+ if (local === '*' && imported === '*') {
904
+ dep.symbols.set('*', '*', convertLoc(loc), true);
905
+ } else {
906
+ let reExportName =
907
+ dep.symbols.get(imported)?.local ??
908
+ `$${asset.id}$re_export$${local}`;
909
+ asset.symbols.set(local, reExportName);
910
+ dep.symbols.set(imported, reExportName, convertLoc(loc), true);
911
+ }
912
+ }
913
+
914
+ for (let specifier of hoist_result.wrapped_requires) {
915
+ let dep = deps.get(specifier);
916
+ if (!dep) continue;
917
+ dep.meta.shouldWrap = true;
918
+ }
919
+
920
+ for (let name in hoist_result.dynamic_imports) {
921
+ let dep = deps.get(hoist_result.dynamic_imports[name]);
922
+ if (!dep) continue;
923
+ dep.meta.promiseSymbol = name;
924
+ }
925
+
926
+ if (hoist_result.self_references.length > 0) {
927
+ let symbols = new Map();
928
+ for (let name of hoist_result.self_references) {
929
+ // Do not create a self-reference for the `default` symbol unless we have seen an __esModule flag.
930
+ if (
931
+ name === 'default' &&
932
+ !asset.symbols.hasExportSymbol('__esModule')
933
+ ) {
934
+ continue;
935
+ }
936
+
937
+ let local = nullthrows(asset.symbols.get(name)).local;
938
+ symbols.set(name, {
939
+ local,
940
+ isWeak: false,
941
+ loc: null,
942
+ });
943
+ }
944
+
945
+ // Use the asset id as a unique key if one has not already been set.
946
+ // This lets us create a dependency on the asset itself by using it as a specifier.
947
+ // Using the unique key ensures that the dependency always resolves to the correct asset,
948
+ // even if it came from a transformer that produced multiple assets (e.g. css modules).
949
+ // Also avoids needing a resolution request.
950
+ asset.uniqueKey ||= asset.id;
951
+ asset.addDependency({
952
+ specifier: asset.uniqueKey,
953
+ specifierType: 'esm',
954
+ symbols,
955
+ });
956
+ }
957
+
958
+ // Add * symbol if there are CJS exports, no imports/exports at all
959
+ // (and the asset has side effects), or the asset is wrapped.
960
+ // This allows accessing symbols that don't exist without errors in symbol propagation.
961
+ if (
962
+ hoist_result.has_cjs_exports ||
963
+ (!hoist_result.is_esm &&
964
+ asset.sideEffects &&
965
+ deps.size === 0 &&
966
+ Object.keys(hoist_result.exported_symbols).length === 0) ||
967
+ (hoist_result.should_wrap && !asset.symbols.hasExportSymbol('*'))
968
+ ) {
969
+ asset.symbols.set('*', `$${asset.id}$exports`);
970
+ }
971
+
972
+ asset.meta.hasCJSExports = hoist_result.has_cjs_exports;
973
+ asset.meta.staticExports = hoist_result.static_cjs_exports;
974
+ asset.meta.shouldWrap = hoist_result.should_wrap;
975
+ } else {
976
+ if (symbol_result) {
977
+ let deps = new Map(
978
+ asset
979
+ .getDependencies()
980
+ .map(dep => [dep.meta.placeholder ?? dep.specifier, dep]),
981
+ );
982
+ asset.symbols.ensure();
983
+
984
+ for (let {exported, local, loc, source} of symbol_result.exports) {
985
+ let dep = source ? deps.get(source) : undefined;
986
+ asset.symbols.set(
987
+ exported,
988
+ `${dep?.id ?? ''}$${local}`,
989
+ convertLoc(loc),
990
+ );
991
+ if (dep != null) {
992
+ dep.symbols.ensure();
993
+ dep.symbols.set(
994
+ local,
995
+ `${dep?.id ?? ''}$${local}`,
996
+ convertLoc(loc),
997
+ true,
998
+ );
999
+ }
1000
+ }
1001
+
1002
+ for (let {source, local, imported, loc} of symbol_result.imports) {
1003
+ let dep = deps.get(source);
1004
+ if (!dep) continue;
1005
+ dep.symbols.ensure();
1006
+ dep.symbols.set(imported, local, convertLoc(loc));
1007
+ }
1008
+
1009
+ for (let {source, loc} of symbol_result.exports_all) {
1010
+ let dep = deps.get(source);
1011
+ if (!dep) continue;
1012
+ dep.symbols.ensure();
1013
+ dep.symbols.set('*', '*', convertLoc(loc), true);
1014
+ }
1015
+
1016
+ // Add * symbol if there are CJS exports, no imports/exports at all, or the asset is wrapped.
1017
+ // This allows accessing symbols that don't exist without errors in symbol propagation.
1018
+ if (
1019
+ symbol_result.has_cjs_exports ||
1020
+ (!symbol_result.is_esm &&
1021
+ deps.size === 0 &&
1022
+ symbol_result.exports.length === 0) ||
1023
+ (symbol_result.should_wrap && !asset.symbols.hasExportSymbol('*'))
1024
+ ) {
1025
+ asset.symbols.ensure();
1026
+ asset.symbols.set('*', `$${asset.id}$exports`);
1027
+ }
1028
+ } else {
1029
+ // If the asset is wrapped, add * as a fallback
1030
+ asset.symbols.ensure();
1031
+ asset.symbols.set('*', `$${asset.id}$exports`);
1032
+ }
1033
+
1034
+ // For all other imports and requires, mark everything as imported (this covers both dynamic
1035
+ // imports and non-top-level requires.)
1036
+ for (let dep of asset.getDependencies()) {
1037
+ if (dep.symbols.isCleared) {
1038
+ dep.symbols.ensure();
1039
+ dep.symbols.set('*', `${dep.id}$`);
1040
+ }
1041
+ }
1042
+
1043
+ if (needs_esm_helpers) {
1044
+ asset.addDependency({
1045
+ specifier: '@atlaspack/transformer-js/src/esmodule-helpers.js',
1046
+ specifierType: 'esm',
1047
+ resolveFrom: __filename,
1048
+ env: {
1049
+ includeNodeModules: {
1050
+ '@atlaspack/transformer-js': true,
1051
+ },
1052
+ },
1053
+ });
1054
+ }
1055
+ }
1056
+
1057
+ asset.type = 'js';
1058
+ asset.setBuffer(compiledCode);
1059
+
1060
+ if (map) {
1061
+ let sourceMap = new SourceMap(options.projectRoot);
1062
+ sourceMap.addVLQMap(JSON.parse(map));
1063
+ if (originalMap) {
1064
+ sourceMap.extends(originalMap);
1065
+ }
1066
+ asset.setMap(sourceMap);
1067
+ }
1068
+
1069
+ return [asset, ...macroAssets];
1070
+ },
1071
+ }): Transformer);