@wyw-in-js/transform 1.0.6 → 1.0.7

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 (77) hide show
  1. package/esm/cache.js +100 -7
  2. package/esm/cache.js.map +1 -1
  3. package/esm/debug/fileReporter.js.map +1 -1
  4. package/esm/module.js +51 -2
  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 +60 -17
  13. package/esm/transform/Entrypoint.js.map +1 -1
  14. package/esm/transform/EvaluatedEntrypoint.js.map +1 -1
  15. package/esm/transform/barrelManifest.js +291 -0
  16. package/esm/transform/barrelManifest.js.map +1 -0
  17. package/esm/transform/generators/getExports.js +5 -0
  18. package/esm/transform/generators/getExports.js.map +1 -1
  19. package/esm/transform/generators/processEntrypoint.js +27 -1
  20. package/esm/transform/generators/processEntrypoint.js.map +1 -1
  21. package/esm/transform/generators/resolveImports.js +29 -5
  22. package/esm/transform/generators/resolveImports.js.map +1 -1
  23. package/esm/transform/generators/rewriteBarrelImports.js +733 -0
  24. package/esm/transform/generators/rewriteBarrelImports.js.map +1 -0
  25. package/esm/transform/generators/transform.js +154 -21
  26. package/esm/transform/generators/transform.js.map +1 -1
  27. package/esm/transform/types.js.map +1 -1
  28. package/lib/cache.js +103 -7
  29. package/lib/cache.js.map +1 -1
  30. package/lib/debug/fileReporter.js.map +1 -1
  31. package/lib/module.js +51 -2
  32. package/lib/module.js.map +1 -1
  33. package/lib/plugins/shaker.js +152 -13
  34. package/lib/plugins/shaker.js.map +1 -1
  35. package/lib/shaker.js +58 -26
  36. package/lib/shaker.js.map +1 -1
  37. package/lib/transform/BaseEntrypoint.js +3 -1
  38. package/lib/transform/BaseEntrypoint.js.map +1 -1
  39. package/lib/transform/Entrypoint.js +61 -17
  40. package/lib/transform/Entrypoint.js.map +1 -1
  41. package/lib/transform/EvaluatedEntrypoint.js.map +1 -1
  42. package/lib/transform/barrelManifest.js +300 -0
  43. package/lib/transform/barrelManifest.js.map +1 -0
  44. package/lib/transform/generators/getExports.js +5 -0
  45. package/lib/transform/generators/getExports.js.map +1 -1
  46. package/lib/transform/generators/processEntrypoint.js +27 -1
  47. package/lib/transform/generators/processEntrypoint.js.map +1 -1
  48. package/lib/transform/generators/resolveImports.js +29 -5
  49. package/lib/transform/generators/resolveImports.js.map +1 -1
  50. package/lib/transform/generators/rewriteBarrelImports.js +743 -0
  51. package/lib/transform/generators/rewriteBarrelImports.js.map +1 -0
  52. package/lib/transform/generators/transform.js +158 -22
  53. package/lib/transform/generators/transform.js.map +1 -1
  54. package/lib/transform/types.js.map +1 -1
  55. package/package.json +8 -4
  56. package/types/cache.d.ts +16 -2
  57. package/types/cache.js +111 -7
  58. package/types/debug/fileReporter.d.ts +1 -0
  59. package/types/module.d.ts +3 -0
  60. package/types/module.js +57 -2
  61. package/types/plugins/shaker.js +161 -16
  62. package/types/shaker.d.ts +10 -1
  63. package/types/shaker.js +56 -28
  64. package/types/transform/BaseEntrypoint.d.ts +3 -1
  65. package/types/transform/BaseEntrypoint.js +5 -1
  66. package/types/transform/Entrypoint.d.ts +9 -0
  67. package/types/transform/Entrypoint.js +73 -20
  68. package/types/transform/EvaluatedEntrypoint.d.ts +2 -0
  69. package/types/transform/barrelManifest.d.ts +42 -0
  70. package/types/transform/barrelManifest.js +300 -0
  71. package/types/transform/generators/getExports.js +5 -0
  72. package/types/transform/generators/processEntrypoint.js +26 -1
  73. package/types/transform/generators/resolveImports.js +29 -5
  74. package/types/transform/generators/rewriteBarrelImports.d.ts +15 -0
  75. package/types/transform/generators/rewriteBarrelImports.js +815 -0
  76. package/types/transform/generators/transform.js +148 -19
  77. package/types/transform/types.d.ts +2 -0
@@ -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;
45
+ beginProcessing(): void;
41
46
  createAction<TType extends ActionTypes, TAction extends ActionByType<TType>>(actionType: TType, data: TAction['data'], abortSignal?: AbortSignal | null): 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,14 +1,18 @@
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 ===';
13
17
  function hasLoop(name, parent, processed = []) {
14
18
  if (parent.name === name || processed.includes(parent.name)) {
@@ -26,18 +30,24 @@ class Entrypoint extends BaseEntrypoint_1.BaseEntrypoint {
26
30
  initialCode;
27
31
  resolveTasks;
28
32
  dependencies;
33
+ invalidationDependencies;
34
+ invalidateOnDependencyChange;
29
35
  evaluated = false;
30
36
  loadedAndParsed;
31
37
  onSupersedeHandlers = [];
32
38
  actionsCache = new Map();
33
39
  #hasWywMetadata = false;
40
+ #isProcessing = false;
41
+ #pendingOnly = null;
34
42
  #supersededWith = null;
35
43
  #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);
44
+ constructor(services, parents, initialCode, name, only, exports, evaluatedOnly, loadedAndParsed, resolveTasks = new Map(), dependencies = new Map(), invalidationDependencies = new Map(), invalidateOnDependencyChange = new Set(), generation = 1) {
45
+ super(services, evaluatedOnly, exports, generation, name, only, parents, dependencies, invalidationDependencies, invalidateOnDependencyChange);
38
46
  this.initialCode = initialCode;
39
47
  this.resolveTasks = resolveTasks;
40
48
  this.dependencies = dependencies;
49
+ this.invalidationDependencies = invalidationDependencies;
50
+ this.invalidateOnDependencyChange = invalidateOnDependencyChange;
41
51
  this.loadedAndParsed =
42
52
  loadedAndParsed ??
43
53
  services.loadAndParseFn(services, name, initialCode, parents[0]?.log ?? services.log);
@@ -99,9 +109,18 @@ class Entrypoint extends BaseEntrypoint_1.BaseEntrypoint {
99
109
  static innerCreate(services, parent, name, only, loadedCode) {
100
110
  const { cache } = services;
101
111
  const cached = cache.get('entrypoints', name);
102
- const changed = loadedCode !== undefined
103
- ? cache.invalidateIfChanged(name, loadedCode, undefined, 'loaded')
104
- : false;
112
+ let changed = false;
113
+ if (loadedCode !== undefined) {
114
+ changed = cache.invalidateIfChanged(name, loadedCode, undefined, 'loaded');
115
+ }
116
+ else if (cached && cached.initialCode === undefined) {
117
+ try {
118
+ changed = cache.invalidateIfChanged(name, node_fs_1.default.readFileSync((0, parseRequest_1.stripQueryAndHash)(name), 'utf8'), undefined, 'fs');
119
+ }
120
+ catch {
121
+ changed = false;
122
+ }
123
+ }
105
124
  if (!cached?.evaluated && cached?.ignored) {
106
125
  return ['cached', cached];
107
126
  }
@@ -124,20 +143,18 @@ class Entrypoint extends BaseEntrypoint_1.BaseEntrypoint {
124
143
  return [isLoop ? 'loop' : 'cached', cached];
125
144
  }
126
145
  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('*');
146
+ if (cached.#isProcessing) {
147
+ cached.deferOnlySupersede(mergedOnly);
148
+ cached.log('is being processed, defer supersede (%o -> %o)', cached.only, mergedOnly);
149
+ return [isLoop ? 'loop' : 'cached', cached];
139
150
  }
151
+ return [isLoop ? 'loop' : 'created', cached.supersede(mergedOnly)];
140
152
  }
153
+ 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
154
+ ? cached.invalidationDependencies
155
+ : undefined, cached && 'invalidateOnDependencyChange' in cached
156
+ ? cached.invalidateOnDependencyChange
157
+ : undefined, cached ? cached.generation + 1 : 1);
141
158
  if (cached && !cached.evaluated) {
142
159
  cached.log('is cached, but with different code');
143
160
  cached.supersede(newEntrypoint);
@@ -148,9 +165,27 @@ class Entrypoint extends BaseEntrypoint_1.BaseEntrypoint {
148
165
  this.resolveTasks.delete(dependency.source);
149
166
  this.dependencies.set(dependency.source, dependency);
150
167
  }
168
+ addInvalidationDependency(dependency) {
169
+ this.resolveTasks.delete(dependency.source);
170
+ this.invalidationDependencies.set(dependency.source, dependency);
171
+ }
151
172
  addResolveTask(name, dependency) {
152
173
  this.resolveTasks.set(name, dependency);
153
174
  }
175
+ applyDeferredSupersede() {
176
+ if (this.#supersededWith || this.#pendingOnly === null) {
177
+ return null;
178
+ }
179
+ const mergedOnly = (0, Entrypoint_helpers_1.mergeOnly)(this.only, this.#pendingOnly);
180
+ this.#pendingOnly = null;
181
+ if ((0, Entrypoint_helpers_1.isSuperSet)(this.only, mergedOnly)) {
182
+ return null;
183
+ }
184
+ this.log('apply deferred supersede (%o -> %o)', this.only, mergedOnly);
185
+ const nextEntrypoint = this.supersede(mergedOnly);
186
+ this.services.cache.add('entrypoints', this.name, nextEntrypoint);
187
+ return nextEntrypoint;
188
+ }
154
189
  assertNotSuperseded() {
155
190
  if (this.supersededWith) {
156
191
  this.log('superseded');
@@ -163,6 +198,9 @@ class Entrypoint extends BaseEntrypoint_1.BaseEntrypoint {
163
198
  throw new UnprocessedEntrypointError_1.UnprocessedEntrypointError(this.supersededWith ?? this);
164
199
  }
165
200
  }
201
+ beginProcessing() {
202
+ this.#isProcessing = true;
203
+ }
166
204
  createAction(actionType, data, abortSignal = null) {
167
205
  if (!this.actionsCache.has(actionType)) {
168
206
  this.actionsCache.set(actionType, new Map());
@@ -187,13 +225,22 @@ class Entrypoint extends BaseEntrypoint_1.BaseEntrypoint {
187
225
  createEvaluated() {
188
226
  const evaluatedOnly = (0, Entrypoint_helpers_1.mergeOnly)(this.evaluatedOnly, this.only);
189
227
  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);
228
+ 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
229
  evaluated.initialCode = this.initialCode;
192
230
  return evaluated;
193
231
  }
232
+ endProcessing() {
233
+ this.#isProcessing = false;
234
+ }
194
235
  getDependency(name) {
195
236
  return this.dependencies.get(name);
196
237
  }
238
+ getInvalidationDependency(name) {
239
+ return this.invalidationDependencies.get(name);
240
+ }
241
+ markInvalidateOnDependencyChange(filename) {
242
+ this.invalidateOnDependencyChange.add(filename);
243
+ }
197
244
  getResolveTask(name) {
198
245
  return this.resolveTasks.get(name);
199
246
  }
@@ -221,10 +268,16 @@ class Entrypoint extends BaseEntrypoint_1.BaseEntrypoint {
221
268
  type: 'setTransformResult',
222
269
  });
223
270
  }
271
+ deferOnlySupersede(only) {
272
+ this.#pendingOnly = this.#pendingOnly
273
+ ? (0, Entrypoint_helpers_1.mergeOnly)(this.#pendingOnly, only)
274
+ : [...only];
275
+ }
224
276
  supersede(newOnlyOrEntrypoint) {
277
+ this.#pendingOnly = null;
225
278
  const newEntrypoint = newOnlyOrEntrypoint instanceof Entrypoint
226
279
  ? 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);
280
+ : 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
281
  this.services.eventEmitter.entrypointEvent(this.seqId, {
229
282
  type: 'superseded',
230
283
  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
  }
@@ -0,0 +1,42 @@
1
+ import type { File } from '@babel/types';
2
+ export type BarrelSkipReason = 'custom-evaluator' | 'empty' | 'ignored' | 'impure' | 'namespace-barrel' | 'unknown-star';
3
+ export type BarrelBlockedReason = 'ambiguous' | 'cycle' | 'namespace-barrel' | 'unknown-star' | 'unresolved';
4
+ export type BarrelResolvedBinding = {
5
+ imported: string;
6
+ kind: 'named';
7
+ source: string;
8
+ } | {
9
+ kind: 'namespace';
10
+ source: string;
11
+ };
12
+ export type BarrelManifestExport = BarrelResolvedBinding | {
13
+ kind: 'blocked';
14
+ reason: BarrelBlockedReason;
15
+ };
16
+ export type BarrelManifest = {
17
+ complete: boolean;
18
+ exports: Record<string, BarrelManifestExport>;
19
+ kind: 'barrel';
20
+ };
21
+ export type BarrelManifestCacheEntry = BarrelManifest | {
22
+ kind: 'ineligible';
23
+ reason: BarrelSkipReason;
24
+ };
25
+ export type RawBarrelReexport = {
26
+ exported: string;
27
+ imported: string;
28
+ kind: 'named';
29
+ source: string;
30
+ } | {
31
+ exported: string;
32
+ kind: 'namespace';
33
+ source: string;
34
+ };
35
+ export type RawBarrelManifest = {
36
+ complete: boolean;
37
+ explicitExports: string[];
38
+ exportAll: string[];
39
+ kind: 'barrel';
40
+ reexports: RawBarrelReexport[];
41
+ };
42
+ export declare function analyzeBarrelFile(ast: File): BarrelManifestCacheEntry | RawBarrelManifest;
@@ -0,0 +1,300 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.analyzeBarrelFile = analyzeBarrelFile;
7
+ /* eslint-disable no-continue, @typescript-eslint/no-use-before-define */
8
+ const traverse_1 = __importDefault(require("@babel/traverse"));
9
+ const types_1 = require("@babel/types");
10
+ const isTypeOnlyImport = (statement) => {
11
+ if (statement.importKind === 'type') {
12
+ return true;
13
+ }
14
+ if (statement.specifiers.length === 0) {
15
+ return false;
16
+ }
17
+ return statement.specifiers.every((specifier) => specifier.type === 'ImportSpecifier' && specifier.importKind === 'type');
18
+ };
19
+ const isTypeOnlyExport = (statement) => statement.exportKind === 'type';
20
+ const getModuleExportName = (node) => node.type === 'Identifier' ? node.name : node.value;
21
+ const isTypeOnlyStatement = (statement) => {
22
+ switch (statement.type) {
23
+ case 'EmptyStatement':
24
+ case 'TSDeclareFunction':
25
+ case 'TSInterfaceDeclaration':
26
+ case 'TSTypeAliasDeclaration':
27
+ return true;
28
+ default:
29
+ return false;
30
+ }
31
+ };
32
+ function collectExportNamedDeclaration(statement, reexports, explicitExports) {
33
+ if (!statement.source) {
34
+ return isTypeOnlyExport(statement);
35
+ }
36
+ if (isTypeOnlyExport(statement)) {
37
+ return true;
38
+ }
39
+ const source = statement.source.value;
40
+ for (const specifier of statement.specifiers) {
41
+ if (specifier.type === 'ExportSpecifier') {
42
+ if (specifier.exportKind === 'type') {
43
+ continue;
44
+ }
45
+ explicitExports.add(getModuleExportName(specifier.exported));
46
+ reexports.push(getNamedReexport(specifier, source));
47
+ continue;
48
+ }
49
+ if (specifier.type === 'ExportDefaultSpecifier') {
50
+ explicitExports.add(getModuleExportName(specifier.exported));
51
+ reexports.push(getDefaultReexport(specifier, source));
52
+ continue;
53
+ }
54
+ if (specifier.type === 'ExportNamespaceSpecifier') {
55
+ explicitExports.add(getModuleExportName(specifier.exported));
56
+ reexports.push(getNamespaceReexport(specifier, source));
57
+ continue;
58
+ }
59
+ return false;
60
+ }
61
+ return statement.specifiers.length > 0;
62
+ }
63
+ function getNamedReexport(specifier, source) {
64
+ return {
65
+ exported: getModuleExportName(specifier.exported),
66
+ imported: getModuleExportName(specifier.local),
67
+ kind: 'named',
68
+ source,
69
+ };
70
+ }
71
+ function getDefaultReexport(specifier, source) {
72
+ return {
73
+ exported: getModuleExportName(specifier.exported),
74
+ imported: 'default',
75
+ kind: 'named',
76
+ source,
77
+ };
78
+ }
79
+ function getNamespaceReexport(specifier, source) {
80
+ return {
81
+ exported: getModuleExportName(specifier.exported),
82
+ kind: 'namespace',
83
+ source,
84
+ };
85
+ }
86
+ const collectImportBinding = (statement, imports) => {
87
+ if (statement.importKind === 'type') {
88
+ return true;
89
+ }
90
+ if (statement.specifiers.length === 0) {
91
+ return false;
92
+ }
93
+ let sawValueImport = false;
94
+ for (const specifier of statement.specifiers) {
95
+ if (specifier.type === 'ImportSpecifier' &&
96
+ specifier.importKind === 'type') {
97
+ continue;
98
+ }
99
+ sawValueImport = true;
100
+ if (specifier.type === 'ImportSpecifier') {
101
+ imports.set(specifier.local.name, {
102
+ imported: getImportSpecifierName(specifier),
103
+ kind: 'named',
104
+ source: statement.source.value,
105
+ });
106
+ continue;
107
+ }
108
+ if (specifier.type === 'ImportDefaultSpecifier') {
109
+ imports.set(specifier.local.name, {
110
+ imported: 'default',
111
+ kind: 'named',
112
+ source: statement.source.value,
113
+ });
114
+ continue;
115
+ }
116
+ imports.set(specifier.local.name, {
117
+ kind: 'namespace',
118
+ source: statement.source.value,
119
+ });
120
+ }
121
+ return sawValueImport || isTypeOnlyImport(statement);
122
+ };
123
+ const getImportSpecifierName = (specifier) => getModuleExportName(specifier.imported);
124
+ const getLocalDeclarationNames = (declaration) => {
125
+ if (declaration.type === 'VariableDeclaration' ||
126
+ declaration.type === 'FunctionDeclaration' ||
127
+ declaration.type === 'ClassDeclaration') {
128
+ return Object.keys((0, types_1.getBindingIdentifiers)(declaration));
129
+ }
130
+ if (declaration.type === 'TSEnumDeclaration' ||
131
+ declaration.type === 'TSModuleDeclaration') {
132
+ return null;
133
+ }
134
+ return [];
135
+ };
136
+ const collectLocalExportNamedDeclaration = (statement, importedBindings, passthroughCandidates, explicitExports) => {
137
+ let complete = true;
138
+ if (isTypeOnlyExport(statement)) {
139
+ return {
140
+ complete: true,
141
+ ok: true,
142
+ };
143
+ }
144
+ if (statement.declaration) {
145
+ const names = getLocalDeclarationNames(statement.declaration);
146
+ if (names === null) {
147
+ return {
148
+ complete: false,
149
+ ok: false,
150
+ };
151
+ }
152
+ for (const name of names) {
153
+ explicitExports.add(name);
154
+ }
155
+ return {
156
+ complete: names.length === 0,
157
+ ok: true,
158
+ };
159
+ }
160
+ for (const specifier of statement.specifiers) {
161
+ if (specifier.type !== 'ExportSpecifier') {
162
+ return {
163
+ complete: false,
164
+ ok: false,
165
+ };
166
+ }
167
+ if (specifier.exportKind === 'type') {
168
+ continue;
169
+ }
170
+ const exported = getModuleExportName(specifier.exported);
171
+ explicitExports.add(exported);
172
+ if (specifier.local.type !== 'Identifier') {
173
+ complete = false;
174
+ continue;
175
+ }
176
+ if (!importedBindings.has(specifier.local.name)) {
177
+ complete = false;
178
+ continue;
179
+ }
180
+ if (!passthroughCandidates.has(specifier.local.name)) {
181
+ passthroughCandidates.set(specifier.local.name, []);
182
+ }
183
+ passthroughCandidates.get(specifier.local.name).push(exported);
184
+ }
185
+ return {
186
+ complete: complete && statement.specifiers.length > 0,
187
+ ok: true,
188
+ };
189
+ };
190
+ const collectPassthroughReexports = (ast, importedBindings, passthroughCandidates, reexports) => {
191
+ let complete = true;
192
+ const bindingReferenceCounts = new Map();
193
+ (0, traverse_1.default)(ast, {
194
+ Program(path) {
195
+ for (const localName of passthroughCandidates.keys()) {
196
+ bindingReferenceCounts.set(localName, path.scope.getBinding(localName)?.referencePaths.length ?? -1);
197
+ }
198
+ path.stop();
199
+ },
200
+ });
201
+ for (const [localName, exportedNames] of passthroughCandidates) {
202
+ if (bindingReferenceCounts.get(localName) !== exportedNames.length) {
203
+ complete = false;
204
+ continue;
205
+ }
206
+ const imported = importedBindings.get(localName);
207
+ for (const exported of exportedNames) {
208
+ if (imported.kind === 'namespace') {
209
+ reexports.push({
210
+ exported,
211
+ kind: 'namespace',
212
+ source: imported.source,
213
+ });
214
+ continue;
215
+ }
216
+ reexports.push({
217
+ exported,
218
+ imported: imported.imported,
219
+ kind: 'named',
220
+ source: imported.source,
221
+ });
222
+ }
223
+ }
224
+ return {
225
+ complete,
226
+ ok: true,
227
+ };
228
+ };
229
+ function analyzeBarrelProgram(ast) {
230
+ const reexports = [];
231
+ const explicitExports = new Set();
232
+ const exportAll = [];
233
+ const importedBindings = new Map();
234
+ const passthroughCandidates = new Map();
235
+ let complete = true;
236
+ for (const statement of ast.program.body) {
237
+ if (statement.type === 'ImportDeclaration') {
238
+ if (!collectImportBinding(statement, importedBindings)) {
239
+ return null;
240
+ }
241
+ continue;
242
+ }
243
+ if (statement.type === 'ExportNamedDeclaration') {
244
+ if (statement.source) {
245
+ if (!collectExportNamedDeclaration(statement, reexports, explicitExports)) {
246
+ return null;
247
+ }
248
+ continue;
249
+ }
250
+ const localResult = collectLocalExportNamedDeclaration(statement, importedBindings, passthroughCandidates, explicitExports);
251
+ if (!localResult.ok) {
252
+ return null;
253
+ }
254
+ complete = complete && localResult.complete;
255
+ continue;
256
+ }
257
+ if (statement.type === 'ExportAllDeclaration') {
258
+ if (statement.exportKind === 'type') {
259
+ continue;
260
+ }
261
+ if (!statement.source) {
262
+ return null;
263
+ }
264
+ exportAll.push(getExportAllSource(statement));
265
+ continue;
266
+ }
267
+ if (statement.type === 'ExportDefaultDeclaration') {
268
+ return null;
269
+ }
270
+ if (!isTypeOnlyStatement(statement)) {
271
+ return null;
272
+ }
273
+ }
274
+ const passthroughResult = collectPassthroughReexports(ast, importedBindings, passthroughCandidates, reexports);
275
+ if (!passthroughResult.ok) {
276
+ return null;
277
+ }
278
+ complete = complete && passthroughResult.complete;
279
+ if (reexports.length === 0 && exportAll.length === 0) {
280
+ return null;
281
+ }
282
+ return {
283
+ complete,
284
+ explicitExports: [...explicitExports],
285
+ exportAll,
286
+ kind: 'barrel',
287
+ reexports,
288
+ };
289
+ }
290
+ const getExportAllSource = (statement) => statement.source.value;
291
+ function analyzeBarrelFile(ast) {
292
+ const result = analyzeBarrelProgram(ast);
293
+ if (!result) {
294
+ return {
295
+ kind: 'ineligible',
296
+ reason: 'impure',
297
+ };
298
+ }
299
+ return result;
300
+ }
@@ -52,11 +52,16 @@ function* getExports() {
52
52
  const resolvedImports = yield* this.getNext('resolveImports', entrypoint, {
53
53
  imports: new Map(withWildcardReexport.map((i) => [i.source, []])),
54
54
  });
55
+ const dependencyFilenames = resolvedImports.flatMap((dependency) => dependency.resolved ? [dependency.resolved] : []);
55
56
  const importedEntrypoints = findExportsInImports(entrypoint, resolvedImports);
56
57
  for (const importedEntrypoint of importedEntrypoints) {
57
58
  const exports = yield* this.getNext('getExports', importedEntrypoint.entrypoint, undefined);
58
59
  result.push(...exports);
59
60
  }
61
+ cache.add('exports', entrypoint.name, result);
62
+ cache.setCacheDependencies('exports', entrypoint.name, dependencyFilenames);
63
+ entrypoint.log(`exports: %o`, result);
64
+ return result;
60
65
  }
61
66
  entrypoint.log(`exports: %o`, result);
62
67
  cache.add('exports', entrypoint.name, result);
@@ -53,7 +53,17 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
53
53
  });
54
54
  Object.defineProperty(exports, "__esModule", { value: true });
55
55
  exports.processEntrypoint = processEntrypoint;
56
+ const shaker_1 = require("../../shaker");
56
57
  const AbortError_1 = require("../actions/AbortError");
58
+ const barrelManifest_1 = require("../barrelManifest");
59
+ const shouldSkipExplodeReexports = (action) => {
60
+ const { loadedAndParsed } = action.entrypoint;
61
+ if (loadedAndParsed.evaluator !== shaker_1.shaker || !loadedAndParsed.ast) {
62
+ return false;
63
+ }
64
+ const barrelAnalysis = (0, barrelManifest_1.analyzeBarrelFile)(loadedAndParsed.ast);
65
+ return barrelAnalysis.kind === 'barrel' && barrelAnalysis.complete;
66
+ };
57
67
  /**
58
68
  * The first stage of processing an entrypoint.
59
69
  * This stage is responsible for:
@@ -64,14 +74,26 @@ const AbortError_1 = require("../actions/AbortError");
64
74
  function* processEntrypoint() {
65
75
  const { only, log } = this.entrypoint;
66
76
  log('start processing (only: %o)', only);
77
+ this.entrypoint.beginProcessing();
67
78
  try {
68
79
  const env_1 = { stack: [], error: void 0, hasError: false };
69
80
  try {
70
81
  const abortSignal = __addDisposableResource(env_1, this.createAbortSignal(), false);
71
- yield ['explodeReexports', this.entrypoint, undefined, abortSignal];
82
+ if (shouldSkipExplodeReexports(this)) {
83
+ log('skip explodeReexports for pure barrel');
84
+ }
85
+ else {
86
+ yield ['explodeReexports', this.entrypoint, undefined, abortSignal];
87
+ }
72
88
  const result = yield* this.getNext('transform', this.entrypoint, undefined, abortSignal);
73
89
  this.entrypoint.assertNotSuperseded();
74
90
  this.entrypoint.setTransformResult(result);
91
+ const supersededWith = this.entrypoint.applyDeferredSupersede();
92
+ if (supersededWith) {
93
+ log('processing finished, deferred only detected; schedule next attempt');
94
+ yield* this.getNext('processEntrypoint', supersededWith, undefined, null);
95
+ return;
96
+ }
75
97
  log('entrypoint processing finished');
76
98
  }
77
99
  catch (e_1) {
@@ -91,4 +113,7 @@ function* processEntrypoint() {
91
113
  log(`Unhandled error: %O`, e);
92
114
  throw e;
93
115
  }
116
+ finally {
117
+ this.entrypoint.endProcessing();
118
+ }
94
119
  }