@wyw-in-js/transform 1.0.6 → 1.0.8

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.
Files changed (98) hide show
  1. package/esm/cache.js +160 -12
  2. package/esm/cache.js.map +1 -1
  3. package/esm/debug/fileReporter.js.map +1 -1
  4. package/esm/module.js +59 -5
  5. package/esm/module.js.map +1 -1
  6. package/esm/plugins/shaker.js +152 -13
  7. package/esm/plugins/shaker.js.map +1 -1
  8. package/esm/shaker.js +51 -23
  9. package/esm/shaker.js.map +1 -1
  10. package/esm/transform/BaseEntrypoint.js +3 -1
  11. package/esm/transform/BaseEntrypoint.js.map +1 -1
  12. package/esm/transform/Entrypoint.js +68 -20
  13. package/esm/transform/Entrypoint.js.map +1 -1
  14. package/esm/transform/EvaluatedEntrypoint.js.map +1 -1
  15. package/esm/transform/actions/BaseAction.js +2 -1
  16. package/esm/transform/actions/BaseAction.js.map +1 -1
  17. package/esm/transform/actions/actionRunner.js +2 -2
  18. package/esm/transform/actions/actionRunner.js.map +1 -1
  19. package/esm/transform/barrelManifest.js +291 -0
  20. package/esm/transform/barrelManifest.js.map +1 -0
  21. package/esm/transform/generators/getExports.js +5 -0
  22. package/esm/transform/generators/getExports.js.map +1 -1
  23. package/esm/transform/generators/processEntrypoint.js +31 -1
  24. package/esm/transform/generators/processEntrypoint.js.map +1 -1
  25. package/esm/transform/generators/resolveImports.js +29 -5
  26. package/esm/transform/generators/resolveImports.js.map +1 -1
  27. package/esm/transform/generators/rewriteBarrelImports.js +733 -0
  28. package/esm/transform/generators/rewriteBarrelImports.js.map +1 -0
  29. package/esm/transform/generators/transform.js +154 -21
  30. package/esm/transform/generators/transform.js.map +1 -1
  31. package/esm/transform/types.js.map +1 -1
  32. package/esm/transform.js +45 -23
  33. package/esm/transform.js.map +1 -1
  34. package/esm/utils/collectTemplateDependencies.js +9 -0
  35. package/esm/utils/collectTemplateDependencies.js.map +1 -1
  36. package/lib/cache.js +163 -12
  37. package/lib/cache.js.map +1 -1
  38. package/lib/debug/fileReporter.js.map +1 -1
  39. package/lib/module.js +61 -7
  40. package/lib/module.js.map +1 -1
  41. package/lib/plugins/shaker.js +152 -13
  42. package/lib/plugins/shaker.js.map +1 -1
  43. package/lib/shaker.js +58 -26
  44. package/lib/shaker.js.map +1 -1
  45. package/lib/transform/BaseEntrypoint.js +3 -1
  46. package/lib/transform/BaseEntrypoint.js.map +1 -1
  47. package/lib/transform/Entrypoint.js +69 -20
  48. package/lib/transform/Entrypoint.js.map +1 -1
  49. package/lib/transform/EvaluatedEntrypoint.js.map +1 -1
  50. package/lib/transform/actions/BaseAction.js +2 -1
  51. package/lib/transform/actions/BaseAction.js.map +1 -1
  52. package/lib/transform/actions/actionRunner.js +2 -2
  53. package/lib/transform/actions/actionRunner.js.map +1 -1
  54. package/lib/transform/barrelManifest.js +300 -0
  55. package/lib/transform/barrelManifest.js.map +1 -0
  56. package/lib/transform/generators/getExports.js +5 -0
  57. package/lib/transform/generators/getExports.js.map +1 -1
  58. package/lib/transform/generators/processEntrypoint.js +31 -1
  59. package/lib/transform/generators/processEntrypoint.js.map +1 -1
  60. package/lib/transform/generators/resolveImports.js +29 -5
  61. package/lib/transform/generators/resolveImports.js.map +1 -1
  62. package/lib/transform/generators/rewriteBarrelImports.js +743 -0
  63. package/lib/transform/generators/rewriteBarrelImports.js.map +1 -0
  64. package/lib/transform/generators/transform.js +158 -22
  65. package/lib/transform/generators/transform.js.map +1 -1
  66. package/lib/transform/types.js.map +1 -1
  67. package/lib/transform.js +45 -23
  68. package/lib/transform.js.map +1 -1
  69. package/lib/utils/collectTemplateDependencies.js +9 -0
  70. package/lib/utils/collectTemplateDependencies.js.map +1 -1
  71. package/package.json +8 -4
  72. package/types/cache.d.ts +23 -2
  73. package/types/cache.js +170 -10
  74. package/types/debug/fileReporter.d.ts +1 -0
  75. package/types/module.d.ts +3 -0
  76. package/types/module.js +65 -5
  77. package/types/plugins/shaker.js +161 -16
  78. package/types/shaker.d.ts +10 -1
  79. package/types/shaker.js +56 -28
  80. package/types/transform/BaseEntrypoint.d.ts +3 -1
  81. package/types/transform/BaseEntrypoint.js +5 -1
  82. package/types/transform/Entrypoint.d.ts +10 -1
  83. package/types/transform/Entrypoint.js +81 -23
  84. package/types/transform/EvaluatedEntrypoint.d.ts +2 -0
  85. package/types/transform/actions/BaseAction.d.ts +2 -1
  86. package/types/transform/actions/BaseAction.js +3 -1
  87. package/types/transform/actions/actionRunner.js +2 -2
  88. package/types/transform/barrelManifest.d.ts +42 -0
  89. package/types/transform/barrelManifest.js +300 -0
  90. package/types/transform/generators/getExports.js +5 -0
  91. package/types/transform/generators/processEntrypoint.js +29 -1
  92. package/types/transform/generators/resolveImports.js +29 -5
  93. package/types/transform/generators/rewriteBarrelImports.d.ts +15 -0
  94. package/types/transform/generators/rewriteBarrelImports.js +815 -0
  95. package/types/transform/generators/transform.js +148 -19
  96. package/types/transform/types.d.ts +3 -0
  97. package/types/transform.js +47 -23
  98. package/types/utils/collectTemplateDependencies.js +9 -0
@@ -49,6 +49,9 @@ function getBindingForExport(exportPath) {
49
49
  }
50
50
  return getNonParamBinding(exportPath, id.name);
51
51
  }
52
+ if (exportPath.isTSEnumDeclaration()) {
53
+ return getNonParamBinding(exportPath, exportPath.node.id.name);
54
+ }
52
55
  return undefined;
53
56
  }
54
57
  const withoutRemoved = (items) => items.filter(({ local }) => !(0, isRemoved_1.isRemoved)(local));
@@ -119,28 +122,118 @@ const getPropertyAssignmentStatement = (ref, bindingName) => {
119
122
  return statement?.isExpressionStatement() ? statement : null;
120
123
  };
121
124
  const isWithinAliveExport = (ref, aliveExports) => [...aliveExports].some((alive) => alive === ref || alive.isAncestor(ref));
122
- function stripExportKeepDeclaration(path) {
125
+ function getExportPathsForVariableDeclaration(declaration, exports) {
126
+ const exportPaths = [];
127
+ declaration.get('declarations').forEach((declarator) => {
128
+ Object.keys(declarator.getOuterBindingIdentifiers()).forEach((name) => {
129
+ const exportPath = exports[name];
130
+ if (exportPath && !exportPaths.includes(exportPath)) {
131
+ exportPaths.push(exportPath);
132
+ }
133
+ });
134
+ });
135
+ return exportPaths;
136
+ }
137
+ function getDeclaratorExportLiveness(declarator, exports, aliveExports) {
138
+ const bindingNames = Object.keys(declarator.getOuterBindingIdentifiers());
139
+ if (bindingNames.length === 0) {
140
+ return null;
141
+ }
142
+ let liveness = null;
143
+ for (const name of bindingNames) {
144
+ const exportPath = exports[name];
145
+ if (!exportPath) {
146
+ return null;
147
+ }
148
+ const isAlive = aliveExports.has(exportPath);
149
+ if (liveness === null) {
150
+ liveness = isAlive;
151
+ }
152
+ else if (liveness !== isAlive) {
153
+ return null;
154
+ }
155
+ }
156
+ return liveness;
157
+ }
158
+ function hasRuntimeReferencesToEnum(path) {
159
+ if (!path.isTSEnumDeclaration()) {
160
+ return false;
161
+ }
162
+ const enumId = path.get('id');
163
+ const bindingName = enumId.node.name;
164
+ const program = path.scope.getProgramParent().path;
165
+ let hasReference = false;
166
+ program.traverse({
167
+ Identifier(identifier) {
168
+ if (hasReference || identifier.node.name !== bindingName) {
169
+ return;
170
+ }
171
+ if (identifier.findParent((ancestor) => ancestor === path)) {
172
+ return;
173
+ }
174
+ if (identifier.find((ancestor) => ancestor.isTSType() || ancestor.isFlowType())) {
175
+ return;
176
+ }
177
+ if (!identifier.isReferencedIdentifier()) {
178
+ return;
179
+ }
180
+ const localBinding = identifier.scope.getBinding(bindingName);
181
+ if (localBinding && localBinding.path !== enumId) {
182
+ return;
183
+ }
184
+ hasReference = true;
185
+ identifier.stop();
186
+ },
187
+ });
188
+ return hasReference;
189
+ }
190
+ function stripExportKeepDeclaration(path, exports, aliveExports, t) {
123
191
  const exportDeclaration = path.findParent((p) => p.isExportNamedDeclaration());
124
192
  if (!exportDeclaration)
125
- return false;
193
+ return null;
126
194
  const declaration = exportDeclaration.get('declaration');
127
195
  if (!declaration.node)
128
- return false;
196
+ return null;
129
197
  if (declaration.isFunctionDeclaration() ||
130
198
  declaration.isClassDeclaration() ||
131
199
  declaration.isTSEnumDeclaration()) {
132
- exportDeclaration.replaceWith(declaration.node);
133
- return true;
200
+ exportDeclaration.replaceWith(t.cloneNode(declaration.node, true));
201
+ return [path];
134
202
  }
135
203
  if (declaration.isVariableDeclaration()) {
136
204
  const declarators = declaration.get('declarations');
137
- if (declarators.length !== 1) {
138
- return false;
205
+ const staleExportPaths = getExportPathsForVariableDeclaration(declaration, exports);
206
+ if (declarators.length === 1) {
207
+ exportDeclaration.replaceWith(t.cloneNode(declaration.node, true));
208
+ return staleExportPaths.length > 0 ? staleExportPaths : [path];
139
209
  }
140
- exportDeclaration.replaceWith(declaration.node);
141
- return true;
210
+ const segments = [];
211
+ for (const declarator of declarators) {
212
+ const isAlive = getDeclaratorExportLiveness(declarator, exports, aliveExports);
213
+ if (isAlive === null) {
214
+ return null;
215
+ }
216
+ const clonedDeclarator = t.cloneNode(declarator.node, true);
217
+ const currentSegment = segments[segments.length - 1];
218
+ if (currentSegment && currentSegment.alive === isAlive) {
219
+ currentSegment.declarators.push(clonedDeclarator);
220
+ }
221
+ else {
222
+ segments.push({
223
+ alive: isAlive,
224
+ declarators: [clonedDeclarator],
225
+ });
226
+ }
227
+ }
228
+ exportDeclaration.replaceWithMultiple(segments.map(({ alive, declarators: groupedDeclarators }) => {
229
+ const groupedDeclaration = t.variableDeclaration(declaration.node.kind, groupedDeclarators);
230
+ return alive
231
+ ? t.exportNamedDeclaration(groupedDeclaration, [])
232
+ : groupedDeclaration;
233
+ }));
234
+ return staleExportPaths.length > 0 ? staleExportPaths : [path];
142
235
  }
143
- return false;
236
+ return null;
144
237
  }
145
238
  function shakerPlugin(babel, { keepSideEffects = false, ifUnknownExport = 'skip-shaking', importOverrides, onlyExports, root, }) {
146
239
  const shakerLogger = shared_1.logger.extend('shaker');
@@ -377,17 +470,58 @@ function shakerPlugin(babel, { keepSideEffects = false, ifUnknownExport = 'skip-
377
470
  (0, scopeHelpers_1.dereference)(path);
378
471
  dereferenced.push(path);
379
472
  }
380
- if (!deleted.has(path) &&
381
- binding &&
382
- blockingReferences.length > 0 &&
383
- stripExportKeepDeclaration(path)) {
384
- deleted.add(path);
473
+ const strippedEnum = !deleted.has(path) && hasRuntimeReferencesToEnum(path)
474
+ ? stripExportKeepDeclaration(path, exports, aliveExports, babel.types)
475
+ : null;
476
+ if (strippedEnum) {
477
+ strippedEnum.forEach((stalePath) => {
478
+ deleted.add(stalePath);
479
+ });
480
+ changed = true;
481
+ // eslint-disable-next-line no-continue
482
+ continue;
483
+ }
484
+ const strippedWithBlocking = !deleted.has(path) && binding && blockingReferences.length > 0
485
+ ? stripExportKeepDeclaration(path, exports, aliveExports, babel.types)
486
+ : null;
487
+ if (strippedWithBlocking) {
488
+ strippedWithBlocking.forEach((stalePath) => {
489
+ deleted.add(stalePath);
490
+ });
385
491
  changed = true;
386
492
  // eslint-disable-next-line no-continue
387
493
  continue;
388
494
  }
389
495
  if (!deleted.has(path) &&
390
496
  (!binding || blockingReferences.length === 0)) {
497
+ // For variable declaration exports, `path` is the init expression
498
+ // (not the Identifier). Other forDeleting candidates whose init
499
+ // expressions are ancestors of references to this binding can
500
+ // incorrectly filter them out of outerReferences. Those candidates
501
+ // may later survive via stripExportKeepDeclaration, leaving a
502
+ // dangling reference. Strip the export keyword but keep the
503
+ // declaration so the unreferenced sweep removes it only when dead.
504
+ // This only applies to expression paths (variable init), not
505
+ // Identifiers (function/class declarations) which can't be
506
+ // ancestors of external references.
507
+ const strippedWithoutBlocking = binding && !path.isIdentifier()
508
+ ? stripExportKeepDeclaration(path, exports, aliveExports, babel.types)
509
+ : null;
510
+ if (strippedWithoutBlocking) {
511
+ strippedWithoutBlocking.forEach((stalePath) => {
512
+ deleted.add(stalePath);
513
+ });
514
+ if (removableAssignmentStatements.size > 0) {
515
+ for (const statement of removableAssignmentStatements) {
516
+ if (queueForDeleting(statement)) {
517
+ changed = true;
518
+ }
519
+ }
520
+ }
521
+ changed = true;
522
+ // eslint-disable-next-line no-continue
523
+ continue;
524
+ }
391
525
  if (removableAssignmentStatements.size > 0) {
392
526
  for (const statement of removableAssignmentStatements) {
393
527
  if (queueForDeleting(statement)) {
@@ -412,12 +546,23 @@ function shakerPlugin(babel, { keepSideEffects = false, ifUnknownExport = 'skip-
412
546
  }
413
547
  });
414
548
  dereferenced = [];
549
+ // stripExportKeepDeclaration replaces ExportNamedDeclaration with
550
+ // its declaration, creating new AST nodes. The old scope bindings
551
+ // become stale (pointing at disconnected paths). Recrawl so
552
+ // getAllBindings() returns fresh bindings with correct .referenced.
553
+ file.scope.crawl();
415
554
  // Find and mark for deleting all unreferenced variables
416
555
  const unreferenced = Object.values(file.scope.getAllBindings()).filter((i) => !i.referenced);
417
556
  for (const binding of unreferenced) {
418
557
  if (binding.path.isVariableDeclarator()) {
419
558
  const id = binding.path.get('id');
420
- if (!(0, isRemoved_1.isRemoved)(id) && !forDeletingSet.has(id)) {
559
+ // Skip destructured patterns removing the declarator would kill
560
+ // sibling bindings that may still be referenced (e.g. export {B}
561
+ // from `const [A, B] = createContext(...)` when only A is dead).
562
+ if (!id.isArrayPattern() &&
563
+ !id.isObjectPattern() &&
564
+ !(0, isRemoved_1.isRemoved)(id) &&
565
+ !forDeletingSet.has(id)) {
421
566
  // Drop dead variable declarations, e.g. `const foo = make();` when `foo` is no longer referenced.
422
567
  for (const violation of binding.constantViolations) {
423
568
  if (queueForDeleting(violation)) {
package/types/shaker.d.ts CHANGED
@@ -1,3 +1,12 @@
1
- import type { Evaluator } from '@wyw-in-js/shared';
1
+ import type { TransformOptions } from '@babel/core';
2
+ import type { File } from '@babel/types';
3
+ import type { Evaluator, EvaluatorConfig } from '@wyw-in-js/shared';
4
+ type ShakerStageResult = [
5
+ ast: File,
6
+ code: string,
7
+ imports: Map<string, string[]> | null
8
+ ];
9
+ export declare const shakeToESM: (evalConfig: TransformOptions, ast: File, code: string, config: EvaluatorConfig, babel: Parameters<Evaluator>[4]) => ShakerStageResult;
10
+ export declare const emitCommonJS: (evalConfig: TransformOptions, ast: File, code: string, babel: Parameters<Evaluator>[4]) => [ast: File, code: string];
2
11
  export declare const shaker: Evaluator;
3
12
  export default shaker;
package/types/shaker.js CHANGED
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.shaker = void 0;
6
+ exports.shaker = exports.emitCommonJS = exports.shakeToESM = void 0;
7
7
  const shaker_1 = __importDefault(require("./plugins/shaker"));
8
8
  const ShakerMetadata_1 = require("./utils/ShakerMetadata");
9
9
  const getPluginKey_1 = require("./utils/getPluginKey");
@@ -11,6 +11,7 @@ const hasKeyInList = (plugin, list) => {
11
11
  const pluginKey = (0, getPluginKey_1.getPluginKey)(plugin);
12
12
  return pluginKey ? list.some((i) => pluginKey.includes(i)) : false;
13
13
  };
14
+ const isCommonJSPlugin = (plugin) => (0, getPluginKey_1.getPluginKey)(plugin) === 'transform-modules-commonjs';
14
15
  const safeResolve = (id, paths) => {
15
16
  try {
16
17
  return require.resolve(id, {
@@ -21,44 +22,50 @@ const safeResolve = (id, paths) => {
21
22
  return null;
22
23
  }
23
24
  };
24
- const shaker = (evalConfig, ast, code, { highPriorityPlugins, ...config }, babel) => {
25
+ const ensureTypescriptPlugin = (plugins, evalConfig) => {
26
+ if (!evalConfig.filename?.endsWith('.ts') &&
27
+ !evalConfig.filename?.endsWith('.tsx') &&
28
+ !evalConfig.filename?.endsWith('.mts') &&
29
+ !evalConfig.filename?.endsWith('.cts')) {
30
+ return;
31
+ }
32
+ const hasTypescriptPlugin = plugins.some((i) => (0, getPluginKey_1.getPluginKey)(i) === 'transform-typescript');
33
+ if (hasTypescriptPlugin) {
34
+ return;
35
+ }
36
+ const preset = safeResolve('@babel/preset-typescript', [evalConfig.filename]);
37
+ const plugin = safeResolve('@babel/plugin-transform-typescript', [
38
+ evalConfig.filename,
39
+ preset,
40
+ ]);
41
+ if (plugin) {
42
+ plugins.push([plugin, { allowDeclareFields: true }]);
43
+ }
44
+ };
45
+ const createShakerPlugins = (evalConfig, config, includeCommonJS) => {
46
+ const { highPriorityPlugins, ...shakerConfig } = config;
25
47
  const preShakePlugins = evalConfig.plugins?.filter((i) => hasKeyInList(i, highPriorityPlugins)) ??
26
48
  [];
27
49
  const plugins = [
28
50
  ...preShakePlugins,
29
- [shaker_1.default, config],
30
- ...(evalConfig.plugins ?? []).filter((i) => !hasKeyInList(i, highPriorityPlugins)),
51
+ [shaker_1.default, shakerConfig],
52
+ ...(evalConfig.plugins ?? []).filter((i) => !hasKeyInList(i, highPriorityPlugins) && !isCommonJSPlugin(i)),
31
53
  ];
32
- const hasCommonjsPlugin = evalConfig.plugins?.some((i) => (0, getPluginKey_1.getPluginKey)(i) === 'transform-modules-commonjs');
33
- if (!hasCommonjsPlugin) {
54
+ ensureTypescriptPlugin(plugins, evalConfig);
55
+ if (includeCommonJS) {
34
56
  plugins.push(require.resolve('@babel/plugin-transform-modules-commonjs'));
35
57
  }
36
- if (evalConfig.filename?.endsWith('.ts') ||
37
- evalConfig.filename?.endsWith('.tsx') ||
38
- evalConfig.filename?.endsWith('.mts') ||
39
- evalConfig.filename?.endsWith('.cts')) {
40
- const hasTypescriptPlugin = evalConfig.plugins?.some((i) => (0, getPluginKey_1.getPluginKey)(i) === 'transform-typescript');
41
- if (!hasTypescriptPlugin) {
42
- const preset = safeResolve('@babel/preset-typescript', [
43
- evalConfig.filename,
44
- ]);
45
- const plugin = safeResolve('@babel/plugin-transform-typescript', [
46
- evalConfig.filename,
47
- preset,
48
- ]);
49
- if (plugin) {
50
- plugins.push([plugin, { allowDeclareFields: true }]);
51
- }
52
- }
53
- }
54
- const transformOptions = {
58
+ return plugins;
59
+ };
60
+ const shakeToESM = (evalConfig, ast, code, config, babel) => {
61
+ const transformed = babel.transformFromAstSync(ast, code, {
55
62
  ...evalConfig,
63
+ ast: true,
56
64
  caller: {
57
65
  name: 'wyw-in-js',
58
66
  },
59
- plugins,
60
- };
61
- const transformed = babel.transformFromAstSync(ast, code, transformOptions);
67
+ plugins: createShakerPlugins(evalConfig, config, false),
68
+ });
62
69
  if (!transformed || !(0, ShakerMetadata_1.hasShakerMetadata)(transformed.metadata)) {
63
70
  throw new Error(`${evalConfig.filename} has no shaker metadata`);
64
71
  }
@@ -68,5 +75,26 @@ const shaker = (evalConfig, ast, code, { highPriorityPlugins, ...config }, babel
68
75
  transformed.metadata.wywEvaluator.imports,
69
76
  ];
70
77
  };
78
+ exports.shakeToESM = shakeToESM;
79
+ const emitCommonJS = (evalConfig, ast, code, babel) => {
80
+ const transformed = babel.transformFromAstSync(ast, code, {
81
+ ...evalConfig,
82
+ ast: true,
83
+ caller: {
84
+ name: 'wyw-in-js',
85
+ },
86
+ plugins: [require.resolve('@babel/plugin-transform-modules-commonjs')],
87
+ });
88
+ if (!transformed?.ast) {
89
+ throw new Error('Babel transform failed');
90
+ }
91
+ return [transformed.ast, transformed.code ?? ''];
92
+ };
93
+ exports.emitCommonJS = emitCommonJS;
94
+ const shaker = (evalConfig, ast, code, config, babel) => {
95
+ const [esmAst, esmCode, imports] = (0, exports.shakeToESM)(evalConfig, ast, code, config, babel);
96
+ const [, commonJSCode] = (0, exports.emitCommonJS)(evalConfig, esmAst, esmCode, babel);
97
+ return [esmAst, commonJSCode, imports];
98
+ };
71
99
  exports.shaker = shaker;
72
100
  exports.default = exports.shaker;
@@ -12,11 +12,13 @@ export declare abstract class BaseEntrypoint {
12
12
  readonly only: string[];
13
13
  readonly parents: ParentEntrypoint[];
14
14
  readonly dependencies: Map<string, IEntrypointDependency>;
15
+ readonly invalidationDependencies: Map<string, IEntrypointDependency>;
16
+ readonly invalidateOnDependencyChange: Set<string>;
15
17
  static createExports: (log: Debugger) => Record<string | symbol, unknown>;
16
18
  readonly idx: string;
17
19
  readonly log: Debugger;
18
20
  readonly seqId: number;
19
- protected constructor(services: Services, evaluatedOnly: string[], exports: Record<string | symbol, unknown> | undefined, generation: number, name: string, only: string[], parents: ParentEntrypoint[], dependencies: Map<string, IEntrypointDependency>);
21
+ protected constructor(services: Services, evaluatedOnly: string[], exports: Record<string | symbol, unknown> | undefined, generation: number, name: string, only: string[], parents: ParentEntrypoint[], dependencies: Map<string, IEntrypointDependency>, invalidationDependencies: Map<string, IEntrypointDependency>, invalidateOnDependencyChange: Set<string>);
20
22
  get exports(): Record<string | symbol, unknown>;
21
23
  set exports(value: unknown);
22
24
  get ref(): string;
@@ -116,13 +116,15 @@ class BaseEntrypoint {
116
116
  only;
117
117
  parents;
118
118
  dependencies;
119
+ invalidationDependencies;
120
+ invalidateOnDependencyChange;
119
121
  static createExports = exports.createExports;
120
122
  idx;
121
123
  log;
122
124
  // eslint-disable-next-line no-plusplus
123
125
  seqId = entrypointSeqId++;
124
126
  #exports;
125
- constructor(services, evaluatedOnly, exports, generation, name, only, parents, dependencies) {
127
+ constructor(services, evaluatedOnly, exports, generation, name, only, parents, dependencies, invalidationDependencies, invalidateOnDependencyChange) {
126
128
  this.services = services;
127
129
  this.evaluatedOnly = evaluatedOnly;
128
130
  this.generation = generation;
@@ -130,6 +132,8 @@ class BaseEntrypoint {
130
132
  this.only = only;
131
133
  this.parents = parents;
132
134
  this.dependencies = dependencies;
135
+ this.invalidationDependencies = invalidationDependencies;
136
+ this.invalidateOnDependencyChange = invalidateOnDependencyChange;
133
137
  this.idx = (0, getFileIdx_1.getFileIdx)(name);
134
138
  this.log =
135
139
  parents[0]?.log.extend(this.ref, '->') ?? services.log.extend(this.ref);
@@ -10,6 +10,8 @@ export declare class Entrypoint extends BaseEntrypoint {
10
10
  readonly initialCode: string | undefined;
11
11
  protected readonly resolveTasks: Map<string, Promise<IEntrypointDependency>>;
12
12
  readonly dependencies: Map<string, IEntrypointDependency>;
13
+ readonly invalidationDependencies: Map<string, IEntrypointDependency>;
14
+ readonly invalidateOnDependencyChange: Set<string>;
13
15
  readonly evaluated = false;
14
16
  readonly loadedAndParsed: IEntrypointCode | IIgnoredEntrypoint;
15
17
  protected onSupersedeHandlers: Array<(newEntrypoint: Entrypoint) => void>;
@@ -35,16 +37,23 @@ export declare class Entrypoint extends BaseEntrypoint {
35
37
  protected static create(services: Services, parent: ParentEntrypoint | null, name: string, only: string[], loadedCode: string | undefined): Entrypoint | 'loop';
36
38
  private static innerCreate;
37
39
  addDependency(dependency: IEntrypointDependency): void;
40
+ addInvalidationDependency(dependency: IEntrypointDependency): void;
38
41
  addResolveTask(name: string, dependency: Promise<IEntrypointDependency>): void;
42
+ applyDeferredSupersede(): Entrypoint | null;
39
43
  assertNotSuperseded(): void;
40
44
  assertTransformed(): void;
41
- createAction<TType extends ActionTypes, TAction extends ActionByType<TType>>(actionType: TType, data: TAction['data'], abortSignal?: AbortSignal | null): BaseAction<TAction>;
45
+ beginProcessing(): void;
46
+ createAction<TType extends ActionTypes, TAction extends ActionByType<TType>>(actionType: TType, data: TAction['data'], abortSignal?: AbortSignal | null, actionContext?: unknown): BaseAction<TAction>;
42
47
  createChild(name: string, only: string[], loadedCode?: string): Entrypoint | 'loop';
43
48
  createEvaluated(): EvaluatedEntrypoint;
49
+ endProcessing(): void;
44
50
  getDependency(name: string): IEntrypointDependency | undefined;
51
+ getInvalidationDependency(name: string): IEntrypointDependency | undefined;
52
+ markInvalidateOnDependencyChange(filename: string): void;
45
53
  getResolveTask(name: string): Promise<IEntrypointDependency> | undefined;
46
54
  hasWywMetadata(): boolean;
47
55
  onSupersede(callback: (newEntrypoint: Entrypoint) => void): () => void;
48
56
  setTransformResult(res: ITransformFileResult | null): void;
57
+ private deferOnlySupersede;
49
58
  private supersede;
50
59
  }
@@ -1,15 +1,20 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.Entrypoint = void 0;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
4
8
  const ts_invariant_1 = require("ts-invariant");
5
9
  const BaseEntrypoint_1 = require("./BaseEntrypoint");
6
10
  const Entrypoint_helpers_1 = require("./Entrypoint.helpers");
7
- const isStaticallyEvaluatableModule_1 = require("./isStaticallyEvaluatableModule");
8
11
  const EvaluatedEntrypoint_1 = require("./EvaluatedEntrypoint");
9
12
  const AbortError_1 = require("./actions/AbortError");
10
13
  const BaseAction_1 = require("./actions/BaseAction");
11
14
  const UnprocessedEntrypointError_1 = require("./actions/UnprocessedEntrypointError");
15
+ const parseRequest_1 = require("../utils/parseRequest");
12
16
  const EMPTY_FILE = '=== empty file ===';
17
+ const DEFAULT_ACTION_CONTEXT = Symbol('defaultActionContext');
13
18
  function hasLoop(name, parent, processed = []) {
14
19
  if (parent.name === name || processed.includes(parent.name)) {
15
20
  return true;
@@ -26,18 +31,24 @@ class Entrypoint extends BaseEntrypoint_1.BaseEntrypoint {
26
31
  initialCode;
27
32
  resolveTasks;
28
33
  dependencies;
34
+ invalidationDependencies;
35
+ invalidateOnDependencyChange;
29
36
  evaluated = false;
30
37
  loadedAndParsed;
31
38
  onSupersedeHandlers = [];
32
39
  actionsCache = new Map();
33
40
  #hasWywMetadata = false;
41
+ #isProcessing = false;
42
+ #pendingOnly = null;
34
43
  #supersededWith = null;
35
44
  #transformResultCode = null;
36
- constructor(services, parents, initialCode, name, only, exports, evaluatedOnly, loadedAndParsed, resolveTasks = new Map(), dependencies = new Map(), generation = 1) {
37
- super(services, evaluatedOnly, exports, generation, name, only, parents, dependencies);
45
+ constructor(services, parents, initialCode, name, only, exports, evaluatedOnly, loadedAndParsed, resolveTasks = new Map(), dependencies = new Map(), invalidationDependencies = new Map(), invalidateOnDependencyChange = new Set(), generation = 1) {
46
+ super(services, evaluatedOnly, exports, generation, name, only, parents, dependencies, invalidationDependencies, invalidateOnDependencyChange);
38
47
  this.initialCode = initialCode;
39
48
  this.resolveTasks = resolveTasks;
40
49
  this.dependencies = dependencies;
50
+ this.invalidationDependencies = invalidationDependencies;
51
+ this.invalidateOnDependencyChange = invalidateOnDependencyChange;
41
52
  this.loadedAndParsed =
42
53
  loadedAndParsed ??
43
54
  services.loadAndParseFn(services, name, initialCode, parents[0]?.log ?? services.log);
@@ -99,9 +110,18 @@ class Entrypoint extends BaseEntrypoint_1.BaseEntrypoint {
99
110
  static innerCreate(services, parent, name, only, loadedCode) {
100
111
  const { cache } = services;
101
112
  const cached = cache.get('entrypoints', name);
102
- const changed = loadedCode !== undefined
103
- ? cache.invalidateIfChanged(name, loadedCode, undefined, 'loaded')
104
- : false;
113
+ let changed = false;
114
+ if (loadedCode !== undefined) {
115
+ changed = cache.invalidateIfChanged(name, loadedCode, undefined, 'loaded');
116
+ }
117
+ else if (cached && cached.initialCode === undefined) {
118
+ try {
119
+ changed = cache.invalidateIfChanged(name, node_fs_1.default.readFileSync((0, parseRequest_1.stripQueryAndHash)(name), 'utf8'), undefined, 'fs');
120
+ }
121
+ catch {
122
+ changed = false;
123
+ }
124
+ }
105
125
  if (!cached?.evaluated && cached?.ignored) {
106
126
  return ['cached', cached];
107
127
  }
@@ -124,20 +144,18 @@ class Entrypoint extends BaseEntrypoint_1.BaseEntrypoint {
124
144
  return [isLoop ? 'loop' : 'cached', cached];
125
145
  }
126
146
  cached.log('is cached, but with different `only` %o (the cached one %o)', only, cached?.only);
127
- return [isLoop ? 'loop' : 'created', cached.supersede(mergedOnly)];
128
- }
129
- const newEntrypoint = new Entrypoint(services, parent ? [parent] : [], loadedCode, name, mergedOnly, exports, evaluatedOnly, undefined, cached && 'resolveTasks' in cached ? cached.resolveTasks : undefined, cached && 'dependencies' in cached ? cached.dependencies : undefined, cached ? cached.generation + 1 : 1);
130
- if (!newEntrypoint.ignored &&
131
- !newEntrypoint.only.includes('*') &&
132
- !newEntrypoint.only.includes('__wywPreval') &&
133
- !newEntrypoint.only.includes('side-effect')) {
134
- const { ast } = newEntrypoint.loadedAndParsed;
135
- if (ast && (0, isStaticallyEvaluatableModule_1.isStaticallyEvaluatableModule)(ast)) {
136
- newEntrypoint.log('[entrypoint] promote `only` to "*" for statically evaluatable module');
137
- newEntrypoint.only.length = 0;
138
- newEntrypoint.only.push('*');
147
+ if (cached.#isProcessing) {
148
+ cached.deferOnlySupersede(mergedOnly);
149
+ cached.log('is being processed, defer supersede (%o -> %o)', cached.only, mergedOnly);
150
+ return [isLoop ? 'loop' : 'cached', cached];
139
151
  }
152
+ return [isLoop ? 'loop' : 'created', cached.supersede(mergedOnly)];
140
153
  }
154
+ const newEntrypoint = new Entrypoint(services, parent ? [parent] : [], loadedCode, name, mergedOnly, exports, evaluatedOnly, undefined, cached && 'resolveTasks' in cached ? cached.resolveTasks : undefined, cached && 'dependencies' in cached ? cached.dependencies : undefined, cached && 'invalidationDependencies' in cached
155
+ ? cached.invalidationDependencies
156
+ : undefined, cached && 'invalidateOnDependencyChange' in cached
157
+ ? cached.invalidateOnDependencyChange
158
+ : undefined, cached ? cached.generation + 1 : 1);
141
159
  if (cached && !cached.evaluated) {
142
160
  cached.log('is cached, but with different code');
143
161
  cached.supersede(newEntrypoint);
@@ -148,9 +166,27 @@ class Entrypoint extends BaseEntrypoint_1.BaseEntrypoint {
148
166
  this.resolveTasks.delete(dependency.source);
149
167
  this.dependencies.set(dependency.source, dependency);
150
168
  }
169
+ addInvalidationDependency(dependency) {
170
+ this.resolveTasks.delete(dependency.source);
171
+ this.invalidationDependencies.set(dependency.source, dependency);
172
+ }
151
173
  addResolveTask(name, dependency) {
152
174
  this.resolveTasks.set(name, dependency);
153
175
  }
176
+ applyDeferredSupersede() {
177
+ if (this.#supersededWith || this.#pendingOnly === null) {
178
+ return null;
179
+ }
180
+ const mergedOnly = (0, Entrypoint_helpers_1.mergeOnly)(this.only, this.#pendingOnly);
181
+ this.#pendingOnly = null;
182
+ if ((0, Entrypoint_helpers_1.isSuperSet)(this.only, mergedOnly)) {
183
+ return null;
184
+ }
185
+ this.log('apply deferred supersede (%o -> %o)', this.only, mergedOnly);
186
+ const nextEntrypoint = this.supersede(mergedOnly);
187
+ this.services.cache.add('entrypoints', this.name, nextEntrypoint);
188
+ return nextEntrypoint;
189
+ }
154
190
  assertNotSuperseded() {
155
191
  if (this.supersededWith) {
156
192
  this.log('superseded');
@@ -163,16 +199,23 @@ class Entrypoint extends BaseEntrypoint_1.BaseEntrypoint {
163
199
  throw new UnprocessedEntrypointError_1.UnprocessedEntrypointError(this.supersededWith ?? this);
164
200
  }
165
201
  }
166
- createAction(actionType, data, abortSignal = null) {
202
+ beginProcessing() {
203
+ this.#isProcessing = true;
204
+ }
205
+ createAction(actionType, data, abortSignal = null, actionContext = DEFAULT_ACTION_CONTEXT) {
167
206
  if (!this.actionsCache.has(actionType)) {
168
207
  this.actionsCache.set(actionType, new Map());
169
208
  }
170
- const cache = this.actionsCache.get(actionType);
209
+ const contexts = this.actionsCache.get(actionType);
210
+ if (!contexts.has(actionContext)) {
211
+ contexts.set(actionContext, new Map());
212
+ }
213
+ const cache = contexts.get(actionContext);
171
214
  const cached = cache.get(data);
172
215
  if (cached && !cached.abortSignal?.aborted) {
173
216
  return cached;
174
217
  }
175
- const newAction = new BaseAction_1.BaseAction(actionType, this.services, this, data, abortSignal);
218
+ const newAction = new BaseAction_1.BaseAction(actionType, this.services, this, data, abortSignal, actionContext);
176
219
  cache.set(data, newAction);
177
220
  this.services.eventEmitter.entrypointEvent(this.seqId, {
178
221
  type: 'actionCreated',
@@ -187,13 +230,22 @@ class Entrypoint extends BaseEntrypoint_1.BaseEntrypoint {
187
230
  createEvaluated() {
188
231
  const evaluatedOnly = (0, Entrypoint_helpers_1.mergeOnly)(this.evaluatedOnly, this.only);
189
232
  this.log('create EvaluatedEntrypoint for %o', evaluatedOnly);
190
- const evaluated = new EvaluatedEntrypoint_1.EvaluatedEntrypoint(this.services, evaluatedOnly, this.exportsProxy, this.generation + 1, this.name, this.only, this.parents, this.dependencies);
233
+ const evaluated = new EvaluatedEntrypoint_1.EvaluatedEntrypoint(this.services, evaluatedOnly, this.exportsProxy, this.generation + 1, this.name, this.only, this.parents, this.dependencies, this.invalidationDependencies, this.invalidateOnDependencyChange);
191
234
  evaluated.initialCode = this.initialCode;
192
235
  return evaluated;
193
236
  }
237
+ endProcessing() {
238
+ this.#isProcessing = false;
239
+ }
194
240
  getDependency(name) {
195
241
  return this.dependencies.get(name);
196
242
  }
243
+ getInvalidationDependency(name) {
244
+ return this.invalidationDependencies.get(name);
245
+ }
246
+ markInvalidateOnDependencyChange(filename) {
247
+ this.invalidateOnDependencyChange.add(filename);
248
+ }
197
249
  getResolveTask(name) {
198
250
  return this.resolveTasks.get(name);
199
251
  }
@@ -221,10 +273,16 @@ class Entrypoint extends BaseEntrypoint_1.BaseEntrypoint {
221
273
  type: 'setTransformResult',
222
274
  });
223
275
  }
276
+ deferOnlySupersede(only) {
277
+ this.#pendingOnly = this.#pendingOnly
278
+ ? (0, Entrypoint_helpers_1.mergeOnly)(this.#pendingOnly, only)
279
+ : [...only];
280
+ }
224
281
  supersede(newOnlyOrEntrypoint) {
282
+ this.#pendingOnly = null;
225
283
  const newEntrypoint = newOnlyOrEntrypoint instanceof Entrypoint
226
284
  ? newOnlyOrEntrypoint
227
- : new Entrypoint(this.services, this.parents, this.initialCode, this.name, newOnlyOrEntrypoint, this.exports, this.evaluatedOnly, this.loadedAndParsed, this.resolveTasks, this.dependencies, this.generation + 1);
285
+ : new Entrypoint(this.services, this.parents, this.initialCode, this.name, newOnlyOrEntrypoint, this.exports, this.evaluatedOnly, this.loadedAndParsed, this.resolveTasks, this.dependencies, this.invalidationDependencies, this.invalidateOnDependencyChange, this.generation + 1);
228
286
  this.services.eventEmitter.entrypointEvent(this.seqId, {
229
287
  type: 'superseded',
230
288
  with: newEntrypoint.seqId,
@@ -9,6 +9,8 @@ export interface IEvaluatedEntrypoint {
9
9
  generation: number;
10
10
  ignored: false;
11
11
  initialCode?: string;
12
+ invalidationDependencies: Map<string, IEntrypointDependency>;
13
+ invalidateOnDependencyChange: Set<string>;
12
14
  log: Debugger;
13
15
  only: string[];
14
16
  }