@wyw-in-js/transform 1.0.7 → 1.1.0

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 (82) hide show
  1. package/esm/cache.js +60 -5
  2. package/esm/cache.js.map +1 -1
  3. package/esm/index.js +2 -1
  4. package/esm/index.js.map +1 -1
  5. package/esm/module.js +8 -3
  6. package/esm/module.js.map +1 -1
  7. package/esm/transform/Entrypoint.js +8 -3
  8. package/esm/transform/Entrypoint.js.map +1 -1
  9. package/esm/transform/actions/BaseAction.js +2 -1
  10. package/esm/transform/actions/BaseAction.js.map +1 -1
  11. package/esm/transform/actions/actionRunner.js +2 -2
  12. package/esm/transform/actions/actionRunner.js.map +1 -1
  13. package/esm/transform/actions/types.js.map +1 -1
  14. package/esm/transform/generators/processEntrypoint.js +5 -1
  15. package/esm/transform/generators/processEntrypoint.js.map +1 -1
  16. package/esm/transform/generators/workflow.js +11 -0
  17. package/esm/transform/generators/workflow.js.map +1 -1
  18. package/esm/transform/helpers/loadWywOptions.js +1 -0
  19. package/esm/transform/helpers/loadWywOptions.js.map +1 -1
  20. package/esm/transform/types.js.map +1 -1
  21. package/esm/transform.js +45 -23
  22. package/esm/transform.js.map +1 -1
  23. package/esm/types.js.map +1 -1
  24. package/esm/utils/TransformDiagnostics.js +10 -0
  25. package/esm/utils/TransformDiagnostics.js.map +1 -0
  26. package/esm/utils/TransformMetadata.js +20 -0
  27. package/esm/utils/TransformMetadata.js.map +1 -1
  28. package/esm/utils/collectTemplateDependencies.js +9 -0
  29. package/esm/utils/collectTemplateDependencies.js.map +1 -1
  30. package/lib/cache.js +60 -5
  31. package/lib/cache.js.map +1 -1
  32. package/lib/index.js +29 -0
  33. package/lib/index.js.map +1 -1
  34. package/lib/module.js +10 -5
  35. package/lib/module.js.map +1 -1
  36. package/lib/transform/Entrypoint.js +8 -3
  37. package/lib/transform/Entrypoint.js.map +1 -1
  38. package/lib/transform/actions/BaseAction.js +2 -1
  39. package/lib/transform/actions/BaseAction.js.map +1 -1
  40. package/lib/transform/actions/actionRunner.js +2 -2
  41. package/lib/transform/actions/actionRunner.js.map +1 -1
  42. package/lib/transform/actions/types.js.map +1 -1
  43. package/lib/transform/generators/processEntrypoint.js +5 -1
  44. package/lib/transform/generators/processEntrypoint.js.map +1 -1
  45. package/lib/transform/generators/workflow.js +10 -0
  46. package/lib/transform/generators/workflow.js.map +1 -1
  47. package/lib/transform/helpers/loadWywOptions.js +1 -0
  48. package/lib/transform/helpers/loadWywOptions.js.map +1 -1
  49. package/lib/transform/types.js.map +1 -1
  50. package/lib/transform.js +45 -23
  51. package/lib/transform.js.map +1 -1
  52. package/lib/types.js.map +1 -1
  53. package/lib/utils/TransformDiagnostics.js +20 -0
  54. package/lib/utils/TransformDiagnostics.js.map +1 -0
  55. package/lib/utils/TransformMetadata.js +27 -1
  56. package/lib/utils/TransformMetadata.js.map +1 -1
  57. package/lib/utils/collectTemplateDependencies.js +9 -0
  58. package/lib/utils/collectTemplateDependencies.js.map +1 -1
  59. package/package.json +3 -3
  60. package/types/cache.d.ts +7 -0
  61. package/types/cache.js +59 -3
  62. package/types/index.d.ts +4 -2
  63. package/types/index.js +6 -1
  64. package/types/module.js +8 -3
  65. package/types/plugins/collector.d.ts +4 -1
  66. package/types/transform/Entrypoint.d.ts +1 -1
  67. package/types/transform/Entrypoint.js +8 -3
  68. package/types/transform/actions/BaseAction.d.ts +2 -1
  69. package/types/transform/actions/BaseAction.js +3 -1
  70. package/types/transform/actions/actionRunner.js +2 -2
  71. package/types/transform/actions/types.d.ts +4 -0
  72. package/types/transform/generators/processEntrypoint.js +4 -1
  73. package/types/transform/generators/workflow.js +8 -0
  74. package/types/transform/helpers/loadWywOptions.js +1 -0
  75. package/types/transform/types.d.ts +1 -0
  76. package/types/transform.js +47 -23
  77. package/types/types.d.ts +4 -1
  78. package/types/utils/TransformDiagnostics.d.ts +9 -0
  79. package/types/utils/TransformDiagnostics.js +15 -0
  80. package/types/utils/TransformMetadata.d.ts +22 -4
  81. package/types/utils/TransformMetadata.js +24 -1
  82. package/types/utils/collectTemplateDependencies.js +9 -0
package/types/cache.d.ts CHANGED
@@ -25,6 +25,7 @@ export declare class TransformCacheCollection<TEntrypoint extends IBaseCachedEnt
25
25
  readonly exports: Map<string, string[]>;
26
26
  private readonly barrelManifestDependencies;
27
27
  private contentHashes;
28
+ private fileMtimes;
28
29
  private readonly exportDependencies;
29
30
  constructor(caches?: Partial<ICaches<TEntrypoint>>);
30
31
  add<TCache extends CacheNames, TValue extends MapValue<ICaches<TEntrypoint>[TCache]>>(cacheName: TCache, key: string, value: TValue): void;
@@ -40,6 +41,12 @@ export declare class TransformCacheCollection<TEntrypoint extends IBaseCachedEnt
40
41
  private getCachedDependencies;
41
42
  private getDependencyCache;
42
43
  private hasCachedDependencies;
44
+ /**
45
+ * Fast check if a file changed on disk since last seen.
46
+ * Uses mtime as a fast path — only reads the file if mtime differs.
47
+ * Returns true if the file changed (cache was invalidated).
48
+ */
49
+ checkFreshness(filename: string, strippedFilename: string): boolean;
43
50
  private setContentHash;
44
51
  }
45
52
  export {};
package/types/cache.js CHANGED
@@ -12,6 +12,13 @@ const parseRequest_1 = require("./utils/parseRequest");
12
12
  function hashContent(content) {
13
13
  return (0, crypto_1.createHash)('sha256').update(content).digest('hex');
14
14
  }
15
+ function isMissingFileError(error) {
16
+ if (!error || typeof error !== 'object') {
17
+ return false;
18
+ }
19
+ const { code } = error;
20
+ return code === 'ENOENT' || code === 'ENOTDIR';
21
+ }
15
22
  const cacheLogger = shared_1.logger.extend('cache');
16
23
  const cacheNames = ['barrelManifests', 'entrypoints', 'exports'];
17
24
  const loggers = cacheNames.reduce((acc, key) => ({
@@ -24,6 +31,7 @@ class TransformCacheCollection {
24
31
  exports;
25
32
  barrelManifestDependencies = new Map();
26
33
  contentHashes = new Map();
34
+ fileMtimes = new Map();
27
35
  exportDependencies = new Map();
28
36
  constructor(caches = {}) {
29
37
  this.barrelManifests = caches.barrelManifests || new Map();
@@ -157,7 +165,19 @@ class TransformCacheCollection {
157
165
  for (const [, dependency] of dependenciesToCheck) {
158
166
  const dependencyFilename = dependency.resolved;
159
167
  if (dependencyFilename) {
160
- const dependencyContent = node_fs_1.default.readFileSync((0, parseRequest_1.stripQueryAndHash)(dependencyFilename), 'utf8');
168
+ let dependencyContent;
169
+ try {
170
+ dependencyContent = node_fs_1.default.readFileSync((0, parseRequest_1.stripQueryAndHash)(dependencyFilename), 'utf8');
171
+ }
172
+ catch (error) {
173
+ if (!isMissingFileError(error)) {
174
+ throw error;
175
+ }
176
+ this.invalidateForFile(dependencyFilename);
177
+ anyDepChanged = true;
178
+ // eslint-disable-next-line no-continue
179
+ continue;
180
+ }
161
181
  const dependencyChanged = this.invalidateIfChanged(dependencyFilename, dependencyContent, visitedFiles, 'fs', changedFiles);
162
182
  if (dependencyChanged &&
163
183
  invalidateOnDependencyChange?.has(dependencyFilename)) {
@@ -249,13 +269,49 @@ class TransformCacheCollection {
249
269
  hasCachedDependencies(filename) {
250
270
  return this.getCachedDependencies(filename).size > 0;
251
271
  }
272
+ /**
273
+ * Fast check if a file changed on disk since last seen.
274
+ * Uses mtime as a fast path — only reads the file if mtime differs.
275
+ * Returns true if the file changed (cache was invalidated).
276
+ */
277
+ checkFreshness(filename, strippedFilename) {
278
+ try {
279
+ const currentMtime = node_fs_1.default.statSync(strippedFilename).mtimeMs;
280
+ const cachedMtime = this.fileMtimes.get(filename);
281
+ if (cachedMtime !== undefined && currentMtime === cachedMtime) {
282
+ return false;
283
+ }
284
+ const content = node_fs_1.default.readFileSync(strippedFilename, 'utf-8');
285
+ this.fileMtimes.set(filename, currentMtime);
286
+ if (this.invalidateIfChanged(filename, content, undefined, 'fs')) {
287
+ return true;
288
+ }
289
+ return false;
290
+ }
291
+ catch (error) {
292
+ if (!isMissingFileError(error)) {
293
+ throw error;
294
+ }
295
+ this.invalidateForFile(filename);
296
+ return true;
297
+ }
298
+ }
252
299
  setContentHash(filename, source, hash) {
253
300
  const current = this.contentHashes.get(filename);
254
301
  if (current) {
255
302
  current[source] = hash;
256
- return;
257
303
  }
258
- this.contentHashes.set(filename, { [source]: hash });
304
+ else {
305
+ this.contentHashes.set(filename, { [source]: hash });
306
+ }
307
+ if (source === 'fs') {
308
+ try {
309
+ this.fileMtimes.set(filename, node_fs_1.default.statSync((0, parseRequest_1.stripQueryAndHash)(filename)).mtimeMs);
310
+ }
311
+ catch {
312
+ // ignore
313
+ }
314
+ }
259
315
  }
260
316
  }
261
317
  exports.TransformCacheCollection = TransformCacheCollection;
package/types/index.d.ts CHANGED
@@ -3,8 +3,10 @@ export { createFileReporter } from './debug/fileReporter';
3
3
  export type { IFileReporterOptions } from './debug/fileReporter';
4
4
  export { default as babelTransformPlugin } from './plugins/babel-transform';
5
5
  export { default as preeval } from './plugins/preeval';
6
- export { getTransformMetadata, withTransformMetadata, } from './utils/TransformMetadata';
7
- export type { WYWTransformMetadata } from './utils/TransformMetadata';
6
+ export { createTransformManifest, getTransformMetadata, stringifyTransformManifest, toTransformResultMetadata, withTransformMetadata, } from './utils/TransformMetadata';
7
+ export type { WYWTransformManifest, WYWTransformMetadata, WYWTransformProcessorMetadata, WYWTransformResultMetadata, } from './utils/TransformMetadata';
8
+ export { collectTransformDiagnostics } from './utils/TransformDiagnostics';
9
+ export type { WYWTransformDiagnostic } from './utils/TransformDiagnostics';
8
10
  export { Module, DefaultModuleImplementation } from './module';
9
11
  export { default as shaker } from './shaker';
10
12
  export { transform } from './transform';
package/types/index.js CHANGED
@@ -17,7 +17,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
17
17
  return (mod && mod.__esModule) ? mod : { "default": mod };
18
18
  };
19
19
  Object.defineProperty(exports, "__esModule", { value: true });
20
- exports.findIdentifiers = exports.TransformCacheCollection = exports.peek = exports.getVisitorKeys = exports.applyProcessors = exports.getFileIdx = exports.isNode = exports.EventEmitter = exports.withDefaultServices = exports.loadWywOptions = exports.syncResolveImports = exports.asyncResolveImports = exports.transformUrl = exports.Entrypoint = exports.prepareCode = exports.baseHandlers = exports.parseFile = exports.EvaluatedEntrypoint = exports.UnprocessedEntrypointError = exports.isUnprocessedEntrypointError = exports.transform = exports.shaker = exports.DefaultModuleImplementation = exports.Module = exports.withTransformMetadata = exports.getTransformMetadata = exports.preeval = exports.babelTransformPlugin = exports.createFileReporter = exports.slugify = void 0;
20
+ exports.findIdentifiers = exports.TransformCacheCollection = exports.peek = exports.getVisitorKeys = exports.applyProcessors = exports.getFileIdx = exports.isNode = exports.EventEmitter = exports.withDefaultServices = exports.loadWywOptions = exports.syncResolveImports = exports.asyncResolveImports = exports.transformUrl = exports.Entrypoint = exports.prepareCode = exports.baseHandlers = exports.parseFile = exports.EvaluatedEntrypoint = exports.UnprocessedEntrypointError = exports.isUnprocessedEntrypointError = exports.transform = exports.shaker = exports.DefaultModuleImplementation = exports.Module = exports.collectTransformDiagnostics = exports.withTransformMetadata = exports.toTransformResultMetadata = exports.stringifyTransformManifest = exports.getTransformMetadata = exports.createTransformManifest = exports.preeval = exports.babelTransformPlugin = exports.createFileReporter = exports.slugify = void 0;
21
21
  var shared_1 = require("@wyw-in-js/shared");
22
22
  Object.defineProperty(exports, "slugify", { enumerable: true, get: function () { return shared_1.slugify; } });
23
23
  var fileReporter_1 = require("./debug/fileReporter");
@@ -27,8 +27,13 @@ Object.defineProperty(exports, "babelTransformPlugin", { enumerable: true, get:
27
27
  var preeval_1 = require("./plugins/preeval");
28
28
  Object.defineProperty(exports, "preeval", { enumerable: true, get: function () { return __importDefault(preeval_1).default; } });
29
29
  var TransformMetadata_1 = require("./utils/TransformMetadata");
30
+ Object.defineProperty(exports, "createTransformManifest", { enumerable: true, get: function () { return TransformMetadata_1.createTransformManifest; } });
30
31
  Object.defineProperty(exports, "getTransformMetadata", { enumerable: true, get: function () { return TransformMetadata_1.getTransformMetadata; } });
32
+ Object.defineProperty(exports, "stringifyTransformManifest", { enumerable: true, get: function () { return TransformMetadata_1.stringifyTransformManifest; } });
33
+ Object.defineProperty(exports, "toTransformResultMetadata", { enumerable: true, get: function () { return TransformMetadata_1.toTransformResultMetadata; } });
31
34
  Object.defineProperty(exports, "withTransformMetadata", { enumerable: true, get: function () { return TransformMetadata_1.withTransformMetadata; } });
35
+ var TransformDiagnostics_1 = require("./utils/TransformDiagnostics");
36
+ Object.defineProperty(exports, "collectTransformDiagnostics", { enumerable: true, get: function () { return TransformDiagnostics_1.collectTransformDiagnostics; } });
32
37
  var module_1 = require("./module");
33
38
  Object.defineProperty(exports, "Module", { enumerable: true, get: function () { return module_1.Module; } });
34
39
  Object.defineProperty(exports, "DefaultModuleImplementation", { enumerable: true, get: function () { return module_1.DefaultModuleImplementation; } });
package/types/module.js CHANGED
@@ -326,10 +326,15 @@ class Module {
326
326
  if (extension !== '.json' && !this.extensions.includes(extension)) {
327
327
  return null;
328
328
  }
329
- const entrypoint = this.cache.get('entrypoints', filename);
329
+ let entrypoint = this.cache.get('entrypoints', filename);
330
330
  if (entrypoint && (0, Entrypoint_helpers_1.isSuperSet)(entrypoint.evaluatedOnly ?? [], only)) {
331
- log('✅ file has been already evaluated');
332
- return entrypoint;
331
+ if (this.cache.checkFreshness(filename, strippedFilename)) {
332
+ entrypoint = undefined;
333
+ }
334
+ if (entrypoint) {
335
+ log('✅ file has been already evaluated');
336
+ return entrypoint;
337
+ }
333
338
  }
334
339
  if (entrypoint?.ignored) {
335
340
  log('✅ file has been ignored during prepare stage. Original code will be used');
@@ -12,7 +12,10 @@ export declare const filename: string;
12
12
  export declare function collector(file: BabelFile, options: Pick<StrictOptions, 'classNameSlug' | 'displayName' | 'extensions' | 'evaluate' | 'tagResolver'> & {
13
13
  eventEmitter?: EventEmitter;
14
14
  }, values: ValueCache): {
15
- artifacts: import("@wyw-in-js/shared").Artifact[];
15
+ readonly artifacts: import("@wyw-in-js/shared").Artifact[];
16
+ readonly className: string;
17
+ readonly displayName: string;
18
+ readonly location: import("@babel/types").SourceLocation | null;
16
19
  }[];
17
20
  export default function collectorPlugin(babel: Core, options: StrictOptions & {
18
21
  eventEmitter?: EventEmitter;
@@ -43,7 +43,7 @@ export declare class Entrypoint extends BaseEntrypoint {
43
43
  assertNotSuperseded(): void;
44
44
  assertTransformed(): void;
45
45
  beginProcessing(): void;
46
- createAction<TType extends ActionTypes, TAction extends ActionByType<TType>>(actionType: TType, data: TAction['data'], abortSignal?: AbortSignal | null): BaseAction<TAction>;
46
+ createAction<TType extends ActionTypes, TAction extends ActionByType<TType>>(actionType: TType, data: TAction['data'], abortSignal?: AbortSignal | null, actionContext?: unknown): BaseAction<TAction>;
47
47
  createChild(name: string, only: string[], loadedCode?: string): Entrypoint | 'loop';
48
48
  createEvaluated(): EvaluatedEntrypoint;
49
49
  endProcessing(): void;
@@ -14,6 +14,7 @@ const BaseAction_1 = require("./actions/BaseAction");
14
14
  const UnprocessedEntrypointError_1 = require("./actions/UnprocessedEntrypointError");
15
15
  const parseRequest_1 = require("../utils/parseRequest");
16
16
  const EMPTY_FILE = '=== empty file ===';
17
+ const DEFAULT_ACTION_CONTEXT = Symbol('defaultActionContext');
17
18
  function hasLoop(name, parent, processed = []) {
18
19
  if (parent.name === name || processed.includes(parent.name)) {
19
20
  return true;
@@ -201,16 +202,20 @@ class Entrypoint extends BaseEntrypoint_1.BaseEntrypoint {
201
202
  beginProcessing() {
202
203
  this.#isProcessing = true;
203
204
  }
204
- createAction(actionType, data, abortSignal = null) {
205
+ createAction(actionType, data, abortSignal = null, actionContext = DEFAULT_ACTION_CONTEXT) {
205
206
  if (!this.actionsCache.has(actionType)) {
206
207
  this.actionsCache.set(actionType, new Map());
207
208
  }
208
- 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);
209
214
  const cached = cache.get(data);
210
215
  if (cached && !cached.abortSignal?.aborted) {
211
216
  return cached;
212
217
  }
213
- 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);
214
219
  cache.set(data, newAction);
215
220
  this.services.eventEmitter.entrypointEvent(this.seqId, {
216
221
  type: 'actionCreated',
@@ -13,13 +13,14 @@ export declare class BaseAction<TAction extends ActionQueueItem> implements GetB
13
13
  readonly entrypoint: Entrypoint;
14
14
  readonly data: TAction['data'];
15
15
  readonly abortSignal: AbortSignal | null;
16
+ readonly actionContext: unknown;
16
17
  readonly idx: string;
17
18
  result: TypeOfResult<TAction> | typeof Pending;
18
19
  private activeScenario;
19
20
  private activeScenarioError?;
20
21
  private activeScenarioNextResults;
21
22
  private handler;
22
- constructor(type: TAction['type'], services: Services, entrypoint: Entrypoint, data: TAction['data'], abortSignal: AbortSignal | null);
23
+ constructor(type: TAction['type'], services: Services, entrypoint: Entrypoint, data: TAction['data'], abortSignal: AbortSignal | null, actionContext: unknown);
23
24
  get log(): Debugger;
24
25
  get ref(): string;
25
26
  createAbortSignal(): AbortSignal & Disposable;
@@ -11,18 +11,20 @@ class BaseAction {
11
11
  entrypoint;
12
12
  data;
13
13
  abortSignal;
14
+ actionContext;
14
15
  idx;
15
16
  result = types_1.Pending;
16
17
  activeScenario = null;
17
18
  activeScenarioError;
18
19
  activeScenarioNextResults = [];
19
20
  handler = null;
20
- constructor(type, services, entrypoint, data, abortSignal) {
21
+ constructor(type, services, entrypoint, data, abortSignal, actionContext) {
21
22
  this.type = type;
22
23
  this.services = services;
23
24
  this.entrypoint = entrypoint;
24
25
  this.data = data;
25
26
  this.abortSignal = abortSignal;
27
+ this.actionContext = actionContext;
26
28
  actionIdx += 1;
27
29
  this.idx = actionIdx.toString(16).padStart(6, '0');
28
30
  }
@@ -36,7 +36,7 @@ async function asyncActionRunner(action, actionHandlers, stack = [getActionRef(a
36
36
  return result.value;
37
37
  }
38
38
  const [type, entrypoint, data, abortSignal] = result.value;
39
- const nextAction = entrypoint.createAction(type, data, abortSignal);
39
+ const nextAction = entrypoint.createAction(type, data, abortSignal, action.actionContext);
40
40
  try {
41
41
  actionResult = await asyncActionRunner(nextAction, actionHandlers, [
42
42
  ...stack,
@@ -70,7 +70,7 @@ function syncActionRunner(action, actionHandlers, stack = [getActionRef(action.t
70
70
  return result.value;
71
71
  }
72
72
  const [type, entrypoint, data, abortSignal] = result.value;
73
- const nextAction = entrypoint.createAction(type, data, abortSignal);
73
+ const nextAction = entrypoint.createAction(type, data, abortSignal, action.actionContext);
74
74
  try {
75
75
  actionResult = syncActionRunner(nextAction, actionHandlers, [
76
76
  ...stack,
@@ -1,5 +1,7 @@
1
1
  import type { BabelFileResult } from '@babel/core';
2
2
  import type { Replacements, Rules } from '@wyw-in-js/shared';
3
+ import type { WYWTransformDiagnostic } from '../../utils/TransformDiagnostics';
4
+ import type { WYWTransformResultMetadata } from '../../utils/TransformMetadata';
3
5
  export interface IExtracted {
4
6
  cssSourceMapText: string;
5
7
  cssText: string;
@@ -12,4 +14,6 @@ export interface IWorkflowActionNonLinariaResult {
12
14
  }
13
15
  export interface IWorkflowActionLinariaResult extends IExtracted, IWorkflowActionNonLinariaResult {
14
16
  dependencies: string[];
17
+ diagnostics?: WYWTransformDiagnostic[];
18
+ metadata?: WYWTransformResultMetadata;
15
19
  }
@@ -57,7 +57,10 @@ const shaker_1 = require("../../shaker");
57
57
  const AbortError_1 = require("../actions/AbortError");
58
58
  const barrelManifest_1 = require("../barrelManifest");
59
59
  const shouldSkipExplodeReexports = (action) => {
60
- const { loadedAndParsed } = action.entrypoint;
60
+ const { loadedAndParsed, only } = action.entrypoint;
61
+ if (only.length === 1 && only[0] === '__wywPreval') {
62
+ return true;
63
+ }
61
64
  if (loadedAndParsed.evaluator !== shaker_1.shaker || !loadedAndParsed.ast) {
62
65
  return false;
63
66
  }
@@ -2,6 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.workflow = workflow;
4
4
  const AbortError_1 = require("../actions/AbortError");
5
+ const TransformDiagnostics_1 = require("../../utils/TransformDiagnostics");
6
+ const TransformMetadata_1 = require("../../utils/TransformMetadata");
5
7
  /**
6
8
  * The entry point for file processing. Sequentially calls `processEntrypoint`,
7
9
  * `evalFile`, `collect`, and `extract`. Returns the result of transforming
@@ -59,6 +61,10 @@ function* workflow() {
59
61
  sourceMap: collectStageResult.map,
60
62
  };
61
63
  }
64
+ const metadata = options.pluginOptions.outputMetadata
65
+ ? (0, TransformMetadata_1.toTransformResultMetadata)(collectStageResult.metadata, dependencies)
66
+ : null;
67
+ const diagnostics = (0, TransformDiagnostics_1.collectTransformDiagnostics)(entrypoint.name, collectStageResult.metadata.processors);
62
68
  // *** 4th stage
63
69
  const extractStageResult = yield* this.getNext('extract', entrypoint, {
64
70
  processors: collectStageResult.metadata.processors,
@@ -67,6 +73,8 @@ function* workflow() {
67
73
  ...extractStageResult,
68
74
  code: collectStageResult.code ?? '',
69
75
  dependencies,
76
+ ...(diagnostics.length > 0 ? { diagnostics } : {}),
77
+ ...(metadata ? { metadata } : {}),
70
78
  replacements: [
71
79
  ...extractStageResult.replacements,
72
80
  ...collectStageResult.metadata.replacements,
@@ -54,6 +54,7 @@ function loadWywOptions(overrides = defaultOverrides) {
54
54
  displayName: false,
55
55
  evaluate: true,
56
56
  extensions: ['.cjs', '.cts', '.js', '.jsx', '.mjs', '.mts', '.ts', '.tsx'],
57
+ outputMetadata: false,
57
58
  rules: rules ?? [
58
59
  {
59
60
  action: shaker_1.shaker,
@@ -34,6 +34,7 @@ export type AnyIteratorResult<TMode extends 'async' | 'sync', TResult> = {
34
34
  sync: IteratorResult<YieldArg, TResult>;
35
35
  }[TMode];
36
36
  export interface IBaseAction<TAction extends ActionQueueItem, TResult, TData> extends IBaseNode {
37
+ actionContext: unknown;
37
38
  abortSignal: AbortSignal | null;
38
39
  createAbortSignal: () => AbortSignal & Disposable;
39
40
  data: TData;
@@ -21,6 +21,24 @@ const resolveImports_1 = require("./transform/generators/resolveImports");
21
21
  const withDefaultServices_1 = require("./transform/helpers/withDefaultServices");
22
22
  const memoizedSyncResolve = new WeakMap();
23
23
  const memoizedAsyncResolve = new WeakMap();
24
+ const EMPTY_CUSTOM_HANDLERS = {};
25
+ const memoizedActionContexts = new WeakMap();
26
+ const getActionContext = (resolveImportsHandler, customHandlers) => {
27
+ const customHandlersKey = Object.keys(customHandlers).length === 0
28
+ ? EMPTY_CUSTOM_HANDLERS
29
+ : customHandlers;
30
+ let actionContextsByHandlers = memoizedActionContexts.get(resolveImportsHandler);
31
+ if (!actionContextsByHandlers) {
32
+ actionContextsByHandlers = new WeakMap();
33
+ memoizedActionContexts.set(resolveImportsHandler, actionContextsByHandlers);
34
+ }
35
+ let actionContext = actionContextsByHandlers.get(customHandlersKey);
36
+ if (!actionContext) {
37
+ actionContext = {};
38
+ actionContextsByHandlers.set(customHandlersKey, actionContext);
39
+ }
40
+ return actionContext;
41
+ };
24
42
  function transformSync(partialServices, originalCode, syncResolve, customHandlers = {}) {
25
43
  const { options } = partialServices;
26
44
  const pluginOptions = (0, loadWywOptions_1.loadWywOptions)(options.pluginOptions);
@@ -35,6 +53,18 @@ function transformSync(partialServices, originalCode, syncResolve, customHandler
35
53
  // If global cache is disabled, we need to create a new cache for each file
36
54
  services.cache = new cache_1.TransformCacheCollection();
37
55
  }
56
+ if (!memoizedSyncResolve.has(syncResolve)) {
57
+ memoizedSyncResolve.set(syncResolve, function resolveImports() {
58
+ return resolveImports_1.syncResolveImports.call(this, syncResolve);
59
+ });
60
+ }
61
+ const resolveImportsHandler = memoizedSyncResolve.get(syncResolve);
62
+ const actionHandlers = {
63
+ ...generators_1.baseHandlers,
64
+ ...customHandlers,
65
+ resolveImports: resolveImportsHandler,
66
+ };
67
+ const actionContext = getActionContext(resolveImportsHandler, customHandlers);
38
68
  const entrypoint = Entrypoint_1.Entrypoint.createRoot(services, options.filename, ['__wywPreval'], originalCode);
39
69
  if (entrypoint.ignored) {
40
70
  return {
@@ -42,18 +72,9 @@ function transformSync(partialServices, originalCode, syncResolve, customHandler
42
72
  sourceMap: options.inputSourceMap,
43
73
  };
44
74
  }
45
- const workflowAction = entrypoint.createAction('workflow', undefined);
46
- if (!memoizedSyncResolve.has(syncResolve)) {
47
- memoizedSyncResolve.set(syncResolve, function resolveImports() {
48
- return resolveImports_1.syncResolveImports.call(this, syncResolve);
49
- });
50
- }
75
+ const workflowAction = entrypoint.createAction('workflow', undefined, null, actionContext);
51
76
  try {
52
- const result = (0, actionRunner_1.syncActionRunner)(workflowAction, {
53
- ...generators_1.baseHandlers,
54
- ...customHandlers,
55
- resolveImports: memoizedSyncResolve.get(syncResolve),
56
- });
77
+ const result = (0, actionRunner_1.syncActionRunner)(workflowAction, actionHandlers);
57
78
  entrypoint.log('%s is ready', entrypoint.name);
58
79
  return result;
59
80
  }
@@ -92,6 +113,19 @@ async function transform(partialServices, originalCode, asyncResolve, customHand
92
113
  * but the "only" option has changed, the file will be re-processed using
93
114
  * the combined "only" option.
94
115
  */
116
+ if (!memoizedAsyncResolve.has(asyncResolve)) {
117
+ const resolveImports = function resolveImports() {
118
+ return resolveImports_1.asyncResolveImports.call(this, asyncResolve);
119
+ };
120
+ memoizedAsyncResolve.set(asyncResolve, resolveImports);
121
+ }
122
+ const resolveImportsHandler = memoizedAsyncResolve.get(asyncResolve);
123
+ const actionHandlers = {
124
+ ...generators_1.baseHandlers,
125
+ ...customHandlers,
126
+ resolveImports: resolveImportsHandler,
127
+ };
128
+ const actionContext = getActionContext(resolveImportsHandler, customHandlers);
95
129
  const entrypoint = Entrypoint_1.Entrypoint.createRoot(services, options.filename, ['__wywPreval'], originalCode);
96
130
  if (entrypoint.ignored) {
97
131
  return {
@@ -99,19 +133,9 @@ async function transform(partialServices, originalCode, asyncResolve, customHand
99
133
  sourceMap: options.inputSourceMap,
100
134
  };
101
135
  }
102
- const workflowAction = entrypoint.createAction('workflow', undefined);
103
- if (!memoizedAsyncResolve.has(asyncResolve)) {
104
- const resolveImports = function resolveImports() {
105
- return resolveImports_1.asyncResolveImports.call(this, asyncResolve);
106
- };
107
- memoizedAsyncResolve.set(asyncResolve, resolveImports);
108
- }
136
+ const workflowAction = entrypoint.createAction('workflow', undefined, null, actionContext);
109
137
  try {
110
- const result = await (0, actionRunner_1.asyncActionRunner)(workflowAction, {
111
- ...generators_1.baseHandlers,
112
- ...customHandlers,
113
- resolveImports: memoizedAsyncResolve.get(asyncResolve),
114
- });
138
+ const result = await (0, actionRunner_1.asyncActionRunner)(workflowAction, actionHandlers);
115
139
  entrypoint.log('%s is ready', entrypoint.name);
116
140
  return result;
117
141
  }
package/types/types.d.ts CHANGED
@@ -4,7 +4,8 @@ import type { File, Program } from '@babel/types';
4
4
  import type { RawSourceMap } from 'source-map';
5
5
  import type { BaseProcessor } from '@wyw-in-js/processor-utils';
6
6
  import type { Debugger, Replacement, Rules, StrictOptions } from '@wyw-in-js/shared';
7
- import type { WYWTransformMetadata } from './utils/TransformMetadata';
7
+ import type { WYWTransformMetadata, WYWTransformResultMetadata } from './utils/TransformMetadata';
8
+ import type { WYWTransformDiagnostic } from './utils/TransformDiagnostics';
8
9
  export type PluginOptions = StrictOptions & {
9
10
  configFile?: string | false;
10
11
  stage?: Stage;
@@ -36,6 +37,8 @@ export type Result = {
36
37
  cssSourceMapText?: string;
37
38
  cssText?: string;
38
39
  dependencies?: string[];
40
+ diagnostics?: WYWTransformDiagnostic[];
41
+ metadata?: WYWTransformResultMetadata;
39
42
  replacements?: Replacement[];
40
43
  rules?: Rules;
41
44
  sourceMap?: RawSourceMap | null;
@@ -0,0 +1,9 @@
1
+ import type { BaseProcessor, ProcessorDiagnostic } from '@wyw-in-js/processor-utils';
2
+ type DiagnosticProcessor = Pick<BaseProcessor, 'artifacts' | 'className' | 'displayName'>;
3
+ export type WYWTransformDiagnostic = ProcessorDiagnostic & {
4
+ className: string;
5
+ displayName: string;
6
+ filename: string;
7
+ };
8
+ export declare const collectTransformDiagnostics: (filename: string, processors: DiagnosticProcessor[]) => WYWTransformDiagnostic[];
9
+ export {};
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.collectTransformDiagnostics = void 0;
4
+ const processor_utils_1 = require("@wyw-in-js/processor-utils");
5
+ const collectTransformDiagnostics = (filename, processors) => processors.flatMap((processor) => processor.artifacts
6
+ .filter(processor_utils_1.isProcessorDiagnosticArtifact)
7
+ .map(([, diagnostic]) => ({
8
+ ...diagnostic,
9
+ className: processor.className,
10
+ displayName: processor.displayName,
11
+ end: diagnostic.end ?? null,
12
+ filename,
13
+ start: diagnostic.start ?? null,
14
+ })));
15
+ exports.collectTransformDiagnostics = collectTransformDiagnostics;
@@ -1,13 +1,31 @@
1
- import type { Artifact, Replacement, Rules } from '@wyw-in-js/shared';
1
+ import type { BaseProcessor } from '@wyw-in-js/processor-utils';
2
+ import type { Artifact, Location, Replacement, Rules } from '@wyw-in-js/shared';
3
+ type TransformMetadataProcessor = Pick<BaseProcessor, 'artifacts' | 'className' | 'displayName' | 'location'>;
2
4
  export type WYWTransformMetadata = {
3
5
  dependencies: string[];
4
- processors: {
5
- artifacts: Artifact[];
6
- }[];
6
+ processors: TransformMetadataProcessor[];
7
7
  replacements: Replacement[];
8
8
  rules: Rules;
9
9
  };
10
+ export type WYWTransformProcessorMetadata = {
11
+ artifacts: Artifact[];
12
+ className: string;
13
+ displayName: string;
14
+ start: Location | null | undefined;
15
+ };
16
+ export type WYWTransformResultMetadata = Omit<WYWTransformMetadata, 'processors'> & {
17
+ processors: WYWTransformProcessorMetadata[];
18
+ };
19
+ export type WYWTransformManifest = WYWTransformResultMetadata & {
20
+ cssFile?: string;
21
+ source: string;
22
+ version: 1;
23
+ };
10
24
  export declare const withTransformMetadata: (value: unknown) => value is {
11
25
  wywInJS: WYWTransformMetadata;
12
26
  };
13
27
  export declare const getTransformMetadata: (value: unknown) => WYWTransformMetadata | undefined;
28
+ export declare const toTransformResultMetadata: (metadata: WYWTransformMetadata, dependencies: string[]) => WYWTransformResultMetadata;
29
+ export declare const createTransformManifest: (metadata: WYWTransformResultMetadata, context: Pick<WYWTransformManifest, "cssFile" | "source">) => WYWTransformManifest;
30
+ export declare const stringifyTransformManifest: (manifest: WYWTransformManifest) => string;
31
+ export {};
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getTransformMetadata = exports.withTransformMetadata = void 0;
3
+ exports.stringifyTransformManifest = exports.createTransformManifest = exports.toTransformResultMetadata = exports.getTransformMetadata = exports.withTransformMetadata = void 0;
4
+ const processor_utils_1 = require("@wyw-in-js/processor-utils");
4
5
  const withTransformMetadata = (value) => typeof value === 'object' &&
5
6
  value !== null &&
6
7
  typeof value.wywInJS === 'object';
@@ -15,3 +16,25 @@ const getTransformMetadata = (value) => {
15
16
  return undefined;
16
17
  };
17
18
  exports.getTransformMetadata = getTransformMetadata;
19
+ const toTransformResultMetadata = (metadata, dependencies) => ({
20
+ dependencies,
21
+ processors: metadata.processors.map((processor) => ({
22
+ artifacts: processor.artifacts
23
+ .filter((artifact) => !(0, processor_utils_1.isProcessorDiagnosticArtifact)(artifact))
24
+ .map(([type, data]) => [type, data]),
25
+ className: processor.className,
26
+ displayName: processor.displayName,
27
+ start: processor.location?.start ?? null,
28
+ })),
29
+ replacements: [...metadata.replacements],
30
+ rules: { ...metadata.rules },
31
+ });
32
+ exports.toTransformResultMetadata = toTransformResultMetadata;
33
+ const createTransformManifest = (metadata, context) => ({
34
+ ...metadata,
35
+ ...context,
36
+ version: 1,
37
+ });
38
+ exports.createTransformManifest = createTransformManifest;
39
+ const stringifyTransformManifest = (manifest) => `${JSON.stringify(manifest, null, 2)}\n`;
40
+ exports.stringifyTransformManifest = stringifyTransformManifest;
@@ -18,6 +18,15 @@ const valueToLiteral_1 = require("./valueToLiteral");
18
18
  function staticEval(ex, evaluate = false) {
19
19
  if (!evaluate)
20
20
  return undefined;
21
+ if (ex.isIdentifier()) {
22
+ const binding = ex.scope.getBinding(ex.node.name);
23
+ // Babel may "evaluate" a destructured binding to its source container
24
+ // object/array instead of the bound value, which changes template semantics.
25
+ if (binding?.path.isVariableDeclarator() &&
26
+ !binding.path.get('id').isIdentifier()) {
27
+ return undefined;
28
+ }
29
+ }
21
30
  const result = ex.evaluate();
22
31
  if (result.confident && !(0, shared_1.hasEvalMeta)(result.value)) {
23
32
  return [result.value];