@decohere/forge 0.0.1-dev.111 → 0.0.1-dev.113

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 (59) hide show
  1. package/README.md +2 -0
  2. package/commonjs/src/actions/index.js +1 -0
  3. package/commonjs/src/actions/paths.action.js +109 -0
  4. package/commonjs/src/commands/index.js +1 -0
  5. package/commonjs/src/commands/paths.command.js +24 -0
  6. package/commonjs/src/extensions/project.extensions.js +1 -1
  7. package/commonjs/src/factories/swc-config.factory.js +10 -1
  8. package/commonjs/src/forge.module.js +2 -0
  9. package/commonjs/src/index.js +4 -0
  10. package/commonjs/src/packaging/release-exports.js +4 -2
  11. package/commonjs/src/services/file-writer.service.js +0 -7
  12. package/commonjs/src/services/incremental-watch.js +24 -0
  13. package/commonjs/src/services/output-cache.js +39 -0
  14. package/commonjs/src/services/swc-transpiler.js +42 -113
  15. package/commonjs/src/services/transpile-cache.js +44 -0
  16. package/commonjs/src/services/type-emitter.js +13 -6
  17. package/commonjs/src/types/transform-response.model.js +3 -16
  18. package/commonjs/src/vitest/forge-build.plugin.js +25 -40
  19. package/es6/src/actions/index.js +1 -0
  20. package/es6/src/actions/paths.action.js +86 -0
  21. package/es6/src/commands/index.js +1 -0
  22. package/es6/src/commands/paths.command.js +14 -0
  23. package/es6/src/extensions/project.extensions.js +1 -1
  24. package/es6/src/factories/swc-config.factory.js +10 -1
  25. package/es6/src/forge.module.js +2 -0
  26. package/es6/src/index.js +1 -0
  27. package/es6/src/packaging/release-exports.js +4 -2
  28. package/es6/src/services/file-writer.service.js +0 -7
  29. package/es6/src/services/incremental-watch.js +18 -0
  30. package/es6/src/services/output-cache.js +29 -0
  31. package/es6/src/services/swc-transpiler.js +42 -113
  32. package/es6/src/services/transpile-cache.js +29 -0
  33. package/es6/src/services/type-emitter.js +13 -6
  34. package/es6/src/types/transform-response.model.js +0 -5
  35. package/es6/src/vitest/forge-build.plugin.js +26 -41
  36. package/package.json +5 -5
  37. package/types/src/actions/index.d.ts +1 -0
  38. package/types/src/actions/paths.action.d.ts +28 -0
  39. package/types/src/commands/index.d.ts +1 -0
  40. package/types/src/commands/paths.command.d.ts +6 -0
  41. package/types/src/factories/swc-config.factory.d.ts +1 -1
  42. package/types/src/forge.module.d.ts +2 -0
  43. package/types/src/index.d.ts +1 -0
  44. package/types/src/packaging/release-exports.d.ts +2 -2
  45. package/types/src/services/file-writer.service.d.ts +0 -1
  46. package/types/src/services/incremental-watch.d.ts +13 -0
  47. package/types/src/services/output-cache.d.ts +10 -0
  48. package/types/src/services/swc-transpiler.d.ts +14 -20
  49. package/types/src/services/transpile-cache.d.ts +17 -0
  50. package/types/src/services/type-emitter.d.ts +6 -7
  51. package/types/src/types/transform-response.model.d.ts +0 -5
  52. package/types/src/types/transformer.type.d.ts +0 -9
  53. package/types/src/vitest/forge-build.plugin.d.ts +0 -2
  54. package/commonjs/src/mixins/caches-outputs.mixin.js +0 -42
  55. package/commonjs/src/mixins/incremental-transform.mixin.js +0 -34
  56. package/es6/src/mixins/caches-outputs.mixin.js +0 -32
  57. package/es6/src/mixins/incremental-transform.mixin.js +0 -24
  58. package/types/src/mixins/caches-outputs.mixin.d.ts +0 -7
  59. package/types/src/mixins/incremental-transform.mixin.d.ts +0 -13
package/README.md CHANGED
@@ -10,6 +10,8 @@ Build system (SWC). `forge build --configuration debug` rewrites workspace impor
10
10
 
11
11
  Forge debug (`aliasPaths: true`) asks Mesh for a map of `package.json` `name` → source `main` (`Workspace.getPackageAliases`) and feeds it to SWC as `jsc.paths`. Flux uses the same map in its resolve hook only, and compiles without `jsc.paths` or `resolveFully`, so imports are resolved once (#513).
12
12
 
13
+ `tsc` can't read Mesh's map, and without `node_modules` links it finds workspace packages only through tsconfig `paths`. `forge paths` writes the map to `tsconfig.paths.json` for the root tsconfig to extend, and `forge paths --check` fails when the file is out of date (#518).
14
+
13
15
  That is a **debug** rule. Release does not rewrite aliases: production must resolve like any other Node app (installed packages, `exports` / `main` pointing at emitted JS).
14
16
 
15
17
  ### Why aliases instead of workspace symlinks
@@ -11,6 +11,7 @@ _export_star(require("./dependency-sync.action.js"), exports);
11
11
  _export_star(require("./docker.action.js"), exports);
12
12
  _export_star(require("./link.action.js"), exports);
13
13
  _export_star(require("./package.action.js"), exports);
14
+ _export_star(require("./paths.action.js"), exports);
14
15
  _export_star(require("./publish.action.js"), exports);
15
16
  _export_star(require("./sync-lock-file.action.js"), exports);
16
17
  _export_star(require("./tag.action.js"), exports);
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ function _export(target, all) {
6
+ for(var name in all)Object.defineProperty(target, name, {
7
+ enumerable: true,
8
+ get: Object.getOwnPropertyDescriptor(all, name).get
9
+ });
10
+ }
11
+ _export(exports, {
12
+ get PATHS_FILE () {
13
+ return PATHS_FILE;
14
+ },
15
+ get Paths () {
16
+ return Paths;
17
+ }
18
+ });
19
+ const _nodefs = require("node:fs");
20
+ const _promises = require("node:fs/promises");
21
+ const _nodepath = /*#__PURE__*/ _interop_require_default(require("node:path"));
22
+ const _decorators = require("@decohere/crucible/decorators");
23
+ const _mesh = require("@decohere/mesh");
24
+ const _cli = require("@decohere/nexus/cli");
25
+ const _scribe = require("@decohere/scribe");
26
+ function _interop_require_default(obj) {
27
+ return obj && obj.__esModule ? obj : {
28
+ default: obj
29
+ };
30
+ }
31
+ function _ts_decorate(decorators, target, key, desc) {
32
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
33
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
34
+ else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
35
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
36
+ }
37
+ function _ts_metadata(k, v) {
38
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
39
+ }
40
+ const PATHS_FILE = "tsconfig.paths.json";
41
+ class Paths {
42
+ workspace;
43
+ #logger = _scribe.scribe.logger(Paths.name);
44
+ constructor(workspace){
45
+ this.workspace = workspace;
46
+ }
47
+ async run(options) {
48
+ const file = _nodepath.default.join(this.workspace.rootDir, PATHS_FILE);
49
+ const content = Paths.render(this.workspace);
50
+ const current = (0, _nodefs.existsSync)(file) ? await (0, _promises.readFile)(file, "utf8") : null;
51
+ const changed = current !== content;
52
+ if (options.check) {
53
+ if (!changed) {
54
+ return _cli.ActionResponse.Success(Paths.name, [
55
+ {
56
+ file,
57
+ changed,
58
+ succeeded: true
59
+ }
60
+ ]);
61
+ }
62
+ const error = `${PATHS_FILE} is out of date. Run \`forge paths\` to update it.`;
63
+ this.#logger.error(error);
64
+ return _cli.ActionResponse.Error(Paths.name, [
65
+ {
66
+ file,
67
+ changed,
68
+ succeeded: false,
69
+ error
70
+ }
71
+ ]);
72
+ }
73
+ if (changed) {
74
+ await (0, _promises.writeFile)(file, content);
75
+ this.#logger.info(`Wrote ${PATHS_FILE}`);
76
+ }
77
+ return _cli.ActionResponse.Success(Paths.name, [
78
+ {
79
+ file,
80
+ changed,
81
+ succeeded: true
82
+ }
83
+ ]);
84
+ }
85
+ /** The file's content: convention and exported subpaths only, never the tsconfig's own paths. */ static render(workspace) {
86
+ const rootDir = _nodepath.default.resolve(workspace.rootDir);
87
+ const { paths } = _mesh.packageAliases.build(workspace.getProjects("debug"), {
88
+ baseUrl: rootDir
89
+ });
90
+ const sorted = Object.fromEntries(Object.keys(paths).sort().map((key)=>[
91
+ key,
92
+ (paths[key] ?? []).map((target)=>`./${target}`)
93
+ ]));
94
+ const json = {
95
+ $comment: "Generated by `forge paths` from the workspace packages. Do not edit.",
96
+ compilerOptions: {
97
+ paths: sorted
98
+ }
99
+ };
100
+ return `${JSON.stringify(json, null, 2)}\n`;
101
+ }
102
+ }
103
+ Paths = _ts_decorate([
104
+ (0, _decorators.injectable)(),
105
+ _ts_metadata("design:type", Function),
106
+ _ts_metadata("design:paramtypes", [
107
+ typeof _mesh.Workspace === "undefined" ? Object : _mesh.Workspace
108
+ ])
109
+ ], Paths);
@@ -8,6 +8,7 @@ _export_star(require("./docker.command.js"), exports);
8
8
  _export_star(require("./forge.command.js"), exports);
9
9
  _export_star(require("./link.command.js"), exports);
10
10
  _export_star(require("./package.command.js"), exports);
11
+ _export_star(require("./paths.command.js"), exports);
11
12
  _export_star(require("./publish.command.js"), exports);
12
13
  _export_star(require("./tag.command.js"), exports);
13
14
  _export_star(require("./typecheck.command.js"), exports);
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "PathsCommand", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return PathsCommand;
9
+ }
10
+ });
11
+ const _cli = require("@decohere/nexus/cli");
12
+ const _pathsaction = require("../actions/paths.action.js");
13
+ class PathsCommand extends _cli.DecohereCommand {
14
+ get actions() {
15
+ return [
16
+ _pathsaction.Paths
17
+ ];
18
+ }
19
+ constructor(name){
20
+ super(name);
21
+ this.description("Write tsconfig.paths.json: tsconfig paths for the workspace packages and their exported subpaths");
22
+ this.option("--check", "Fail if tsconfig.paths.json is missing or out of date", false);
23
+ }
24
+ }
@@ -52,7 +52,7 @@ class ProjectExtensions {
52
52
  }
53
53
  static swcConfigPaths(project) {
54
54
  return [
55
- _nodepath.default.join(project.projectDirectory, _const.SWC_CONFIG),
55
+ _nodepath.default.join(project.projectRoot, _const.SWC_CONFIG),
56
56
  _nodepath.default.join(project.rootDir, _const.SWC_CONFIG)
57
57
  ];
58
58
  }
@@ -73,9 +73,18 @@ class SwcConfigFactory {
73
73
  mergeArrays: false
74
74
  }, defaultOptions, options, userConfig ?? {});
75
75
  }
76
- /** Stamps a per-file filename onto a pre-built base config. */ createFileConfig(file, baseConfig) {
76
+ /** Stamps a per-file filename onto a pre-built base config; `.tsx` also turns on JSX (#518). */ createFileConfig(file, baseConfig) {
77
+ const tsx = file.fullPath.endsWith(".tsx");
78
+ const jsc = tsx && baseConfig.jsc?.parser?.syntax === "typescript" ? {
79
+ ...baseConfig.jsc,
80
+ parser: {
81
+ ...baseConfig.jsc.parser,
82
+ tsx: true
83
+ }
84
+ } : baseConfig.jsc;
77
85
  const config = {
78
86
  ...baseConfig,
87
+ jsc,
79
88
  filename: file.fullPath
80
89
  };
81
90
  this.validate(config);
@@ -76,6 +76,7 @@ class ForgeModule extends _cli.ModuleManifest {
76
76
  reportDependencies: _index.ReportDependencies,
77
77
  version: _index.Version,
78
78
  tag: _index.Tag,
79
+ paths: _index.Paths,
79
80
  typecheck: _index.TypeCheck,
80
81
  commitFiles: _index.CommitFiles,
81
82
  link: _index.Link,
@@ -89,6 +90,7 @@ class ForgeModule extends _cli.ModuleManifest {
89
90
  package: _index1.PackageCommand,
90
91
  version: _index1.VersionCommand,
91
92
  tag: _index1.TagCommand,
93
+ paths: _index1.PathsCommand,
92
94
  typecheck: _index1.TypeCheckCommand,
93
95
  link: _index1.LinkCommand,
94
96
  unlink: _index1.UnlinkCommand
@@ -38,11 +38,15 @@ _export(exports, {
38
38
  },
39
39
  get createWorkspace () {
40
40
  return _createworkspace.createWorkspace;
41
+ },
42
+ get transpileCache () {
43
+ return _transpilecache.transpileCache;
41
44
  }
42
45
  });
43
46
  const _createworkspace = require("./create-workspace.js");
44
47
  const _errors = require("./errors.js");
45
48
  const _forgemodule = require("./forge.module.js");
46
49
  const _swctranspiler = require("./services/swc-transpiler.js");
50
+ const _transpilecache = require("./services/transpile-cache.js");
47
51
  const _buildtargettype = require("./types/build-target.type.js");
48
52
  const _workspaceconfig = require("./workspace.config.js");
@@ -8,14 +8,16 @@ Object.defineProperty(exports, "releaseExports", {
8
8
  return releaseExports;
9
9
  }
10
10
  });
11
+ const _mesh = require("@decohere/mesh");
11
12
  const ES6_PREFIX = "./es6/";
12
13
  /** Condition names that can point at runnable ESM, most specific first. `types` never does. */ const RUNTIME_CONDITIONS = [
13
14
  "import",
14
15
  "node",
15
16
  "default"
16
17
  ];
17
- /** `src/main.ts` becomes `src/main.js`, with any leading `./` dropped. */ function toJs(filePath) {
18
- return filePath.replace(/^\.\//, "").replace(/\.ts$/, ".js").replace(/\.mts$/, ".mjs");
18
+ /** `src/main.ts` becomes `src/main.js` (`.mts` `.mjs`, `.cts` `.cjs`), with any leading `./` dropped. */ function toJs(filePath) {
19
+ const file = filePath.replace(/^\.\//, "");
20
+ return _mesh.sourceExtensions.isSource(file) ? _mesh.sourceExtensions.outputPath(file) : file;
19
21
  }
20
22
  /**
21
23
  * The `exports` a library needs once only its `es6/` output is shipped: the library's own
@@ -29,19 +29,12 @@ class FileWriterService {
29
29
  #artifactByPath = new Map();
30
30
  #warmupSignature = "";
31
31
  #warmupPromise;
32
- #initialized = false;
33
32
  #pendingWrites = [];
34
33
  #activeWorkers = 0;
35
34
  #drainResolvers = [];
36
35
  #ensuredDirs = new Set();
37
36
  #ensureDirPromises = new Map();
38
37
  #workerError;
39
- async initialize() {
40
- if (this.#initialized) {
41
- return;
42
- }
43
- this.#initialized = true;
44
- }
45
38
  async warmup(projects) {
46
39
  const outputDirs = this.getOutputDirs(projects);
47
40
  const signature = outputDirs.join("|");
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "IncrementalWatch", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return IncrementalWatch;
9
+ }
10
+ });
11
+ const _rxjs = require("rxjs");
12
+ class IncrementalWatch {
13
+ #results = new _rxjs.Subject();
14
+ get results$() {
15
+ return this.#results.asObservable();
16
+ }
17
+ watch(workspace, handler) {
18
+ workspace.projectFileChanged$.subscribe((evt)=>{
19
+ void handler(evt).then((responses)=>{
20
+ this.#results.next(responses);
21
+ });
22
+ });
23
+ }
24
+ }
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "OutputCache", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return OutputCache;
9
+ }
10
+ });
11
+ const _io = require("@decohere/nexus/io");
12
+ class OutputCache {
13
+ #bySource = new Map();
14
+ get(sourcePath) {
15
+ return this.#bySource.get(sourcePath);
16
+ }
17
+ /** Records the successful outputs, replacing what each of their source files had before. */ record(responses) {
18
+ const bySource = new Map();
19
+ for (const response of responses){
20
+ if (response.outcome !== "success") continue;
21
+ const group = bySource.get(response.file.fullPath) ?? [];
22
+ group.push(response);
23
+ bySource.set(response.file.fullPath, group);
24
+ }
25
+ for (const [sourcePath, group] of bySource){
26
+ this.#bySource.set(sourcePath, group);
27
+ }
28
+ }
29
+ /** Deletes the files a source built to and forgets them. */ delete(sourcePath) {
30
+ const outputs = this.#bySource.get(sourcePath);
31
+ if (outputs == null) return;
32
+ for (const response of outputs){
33
+ if (response.outputPath != null) {
34
+ void _io.io.deleteFile(response.outputPath);
35
+ }
36
+ }
37
+ this.#bySource.delete(sourcePath);
38
+ }
39
+ }
@@ -8,19 +8,18 @@ Object.defineProperty(exports, "SwcTranspiler", {
8
8
  return SwcTranspiler;
9
9
  }
10
10
  });
11
- const _nodefs = /*#__PURE__*/ _interop_require_default(require("node:fs"));
12
11
  const _nodepath = /*#__PURE__*/ _interop_require_default(require("node:path"));
12
+ const _mesh = require("@decohere/mesh");
13
13
  const _caching = require("@decohere/nexus/caching");
14
14
  const _diagnostics = require("@decohere/nexus/diagnostics");
15
- const _extensibility = require("@decohere/nexus/extensibility");
16
15
  const _io = require("@decohere/nexus/io");
17
16
  const _core = require("@swc/core");
18
17
  const _buildtargettype = require("../types/build-target.type.js");
19
18
  require("../extensions/project.extensions.js");
20
19
  const _swcconfigfactory = require("../factories/swc-config.factory.js");
21
- const _cachesoutputsmixin = require("../mixins/caches-outputs.mixin.js");
22
- const _incrementaltransformmixin = require("../mixins/incremental-transform.mixin.js");
23
20
  const _transformresponsemodel = require("../types/transform-response.model.js");
21
+ const _incrementalwatch = require("./incremental-watch.js");
22
+ const _outputcache = require("./output-cache.js");
24
23
  function _interop_require_default(obj) {
25
24
  return obj && obj.__esModule ? obj : {
26
25
  default: obj
@@ -35,16 +34,21 @@ function _ts_decorate(decorators, target, key, desc) {
35
34
  function _ts_metadata(k, v) {
36
35
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
37
36
  }
38
- class SwcTranspiler extends (0, _extensibility.mixwith)(_cachesoutputsmixin.CachesOutputs, _incrementaltransformmixin.IncrementalTransform) {
37
+ class SwcTranspiler {
39
38
  buildTargets = [
40
39
  _buildtargettype.BuildTargets.es6,
41
40
  _buildtargettype.BuildTargets.commonjs
42
41
  ];
43
42
  #swcConfigFactory = new _swcconfigfactory.SwcConfigFactory();
44
43
  #workspace;
44
+ #outputs = new _outputcache.OutputCache();
45
+ #incremental = new _incrementalwatch.IncrementalWatch();
46
+ get incrementalTransform$() {
47
+ return this.#incremental.results$;
48
+ }
45
49
  watch(workspace) {
46
50
  this.#workspace = workspace;
47
- super.watch(workspace);
51
+ this.#incremental.watch(workspace, (evt)=>this.onIncrementalFileEvent(evt));
48
52
  }
49
53
  withAliasContext(options = {}) {
50
54
  return {
@@ -78,42 +82,18 @@ class SwcTranspiler extends (0, _extensibility.mixwith)(_cachesoutputsmixin.Cach
78
82
  return responses.flat();
79
83
  }));
80
84
  const flat = responses.flat();
81
- this.cacheOutputs(flat);
85
+ this.#outputs.record(flat);
82
86
  return flat;
83
87
  }
84
- async transformFile(fileArrayLike, buildTarget, options = {}) {
85
- const files = Array.isArray(fileArrayLike) ? fileArrayLike : [
86
- fileArrayLike
87
- ];
88
- const responses = this.transformTarget(files, buildTarget, options);
89
- return responses;
90
- }
91
88
  /**
92
89
  * The SWC options for one project and target, without a filename. Building them reads the
93
90
  * workspace's aliases and merges configs, so a caller that transpiles many files builds them
94
- * once and passes them to {@link transformFileSync}.
91
+ * once and passes them to {@link transformCode}.
95
92
  */ createBaseConfig(buildTarget, options = {}) {
96
93
  return this.#swcConfigFactory.createBaseConfig(buildTarget, this.withAliasContext(options));
97
94
  }
98
- /**
99
- * Synchronous, single-file variant of `transformFile`.
100
- * Reads the file with `fs.readFileSync` and transpiles via `swc.transformSync`.
101
- * Intended for callers that cannot await (e.g. Node.js `registerHooks` load hook).
102
- * Pass `baseConfig` from {@link createBaseConfig} to skip rebuilding it for every file.
103
- */ transformFileSync(file, buildTarget, options = {}, baseConfig = this.createBaseConfig(buildTarget, options)) {
95
+ /** Sync variant of {@link transformCode}, for the load hook, which cannot await (#514). */ transformCodeSync(content, file, buildTarget, baseConfig) {
104
96
  const swcOptions = this.#swcConfigFactory.createFileConfig(file, baseConfig);
105
- let content;
106
- try {
107
- content = _nodefs.default.readFileSync(file.fullPath, "utf8");
108
- } catch (error) {
109
- return _transformresponsemodel.TransformResponse.Error({
110
- file,
111
- messages: [
112
- String(error)
113
- ],
114
- config: swcOptions
115
- });
116
- }
117
97
  if (content === "") return SwcTranspiler.skippedEmpty(file, swcOptions);
118
98
  try {
119
99
  const transformed = (0, _core.transformSync)(content, swcOptions);
@@ -132,126 +112,75 @@ class SwcTranspiler extends (0, _extensibility.mixwith)(_cachesoutputsmixin.Cach
132
112
  }
133
113
  }
134
114
  /**
135
- * Async variant of {@link transformFileSync}, with the same result for the same file. SWC
136
- * runs it on libuv's thread pool, so many calls in flight compile in parallel (#661).
137
- */ async transformFileAsync(file, buildTarget, options = {}, baseConfig = this.createBaseConfig(buildTarget, options)) {
115
+ * Compiles `content` as the source of `file`, without reading the file: for callers that
116
+ * already hold the code, such as the Vitest plugin, which must compile what Vite passes it
117
+ * rather than an earlier read of the disk (#517). Async SWC, on the thread pool, so many
118
+ * calls in flight compile in parallel (#661). With a `project`, the response carries where
119
+ * the build writes the output.
120
+ */ async transformCode(content, file, buildTarget, baseConfig, project) {
138
121
  const swcOptions = this.#swcConfigFactory.createFileConfig(file, baseConfig);
139
- let content;
140
- try {
141
- content = await _nodefs.default.promises.readFile(file.fullPath, "utf8");
142
- } catch (error) {
143
- return _transformresponsemodel.TransformResponse.Error({
144
- file,
145
- messages: [
146
- String(error)
147
- ],
148
- config: swcOptions
149
- });
150
- }
151
- if (content === "") return SwcTranspiler.skippedEmpty(file, swcOptions);
122
+ if (content === "") return SwcTranspiler.skippedEmpty(file, swcOptions, project);
152
123
  try {
153
124
  const transformed = await (0, _core.transform)(content, swcOptions);
154
125
  if (transformed == null) {
155
126
  throw new Error("swc.transform returned no result");
156
127
  }
157
- return SwcTranspiler.succeeded(file, buildTarget, swcOptions, content, transformed.code);
128
+ return SwcTranspiler.succeeded(file, buildTarget, swcOptions, content, transformed.code, project);
158
129
  } catch (error) {
159
130
  return _transformresponsemodel.TransformResponse.Error({
160
131
  file,
161
132
  messages: [
162
133
  String(error)
163
134
  ],
164
- config: swcOptions
135
+ config: swcOptions,
136
+ project
165
137
  });
166
138
  }
167
139
  }
168
- static skippedEmpty(file, swcOptions) {
140
+ static skippedEmpty(file, swcOptions, project) {
169
141
  return _transformresponsemodel.TransformResponse.Skipped({
170
142
  file,
171
143
  messages: "File is empty",
172
- config: swcOptions
144
+ config: swcOptions,
145
+ project
173
146
  });
174
147
  }
175
- static succeeded(file, buildTarget, swcOptions, content, code) {
176
- const filePath = file.relativePath;
177
- const extName = _nodepath.default.extname(filePath);
178
- const fileName = filePath.replace(new RegExp(`${extName}$`), ".js");
148
+ static succeeded(file, buildTarget, swcOptions, content, code, project) {
149
+ const fileName = _mesh.sourceExtensions.outputPath(file.relativePath);
150
+ const outBaseDir = project?.getOutDir(buildTarget);
151
+ const outputPath = outBaseDir ? _nodepath.default.join(outBaseDir, fileName) : undefined;
179
152
  return _transformresponsemodel.TransformResponse.Success({
180
153
  file,
181
154
  code,
182
155
  sourceContent: content,
183
156
  target: buildTarget,
184
157
  fileName,
185
- config: swcOptions
158
+ outputDir: outputPath ? _nodepath.default.dirname(outputPath) : undefined,
159
+ outputPath,
160
+ config: swcOptions,
161
+ project
186
162
  });
187
163
  }
188
164
  async transformTarget(files, buildTarget, options, project, onOutput) {
189
- const baseConfig = this.#swcConfigFactory.createBaseConfig(buildTarget, this.withAliasContext(options));
190
- return await Promise.all(files.map((file)=>{
191
- const swcOptions = this.#swcConfigFactory.createFileConfig(file, baseConfig);
192
- return this.runTransform(file, buildTarget, swcOptions, project, onOutput);
165
+ const baseConfig = this.createBaseConfig(buildTarget, options);
166
+ return await Promise.all(files.map(async (file)=>{
167
+ const content = await this.getFileContent(file);
168
+ const response = await this.transformCode(content, file, buildTarget, baseConfig, project);
169
+ onOutput?.(response);
170
+ return response;
193
171
  }));
194
172
  }
195
- async runTransform(file, target, swcOptions, project, onOutput) {
196
- const content = await this.getFileContent(file);
197
- if (content === "") {
198
- const skipped = _transformresponsemodel.TransformResponse.Skipped({
199
- file,
200
- messages: "File is empty",
201
- config: swcOptions,
202
- project
203
- });
204
- onOutput?.(skipped);
205
- return skipped;
206
- }
207
- try {
208
- const filePath = file.relativePath;
209
- const extName = _nodepath.default.extname(filePath);
210
- const fileName = filePath.replace(new RegExp(`${extName}$`), ".js");
211
- const outBaseDir = project?.getOutDir(target);
212
- const outputPath = outBaseDir ? _nodepath.default.join(outBaseDir, fileName) : undefined;
213
- const outputDir = outputPath ? _nodepath.default.dirname(outputPath) : undefined;
214
- const transformed = await (0, _core.transform)(content, swcOptions);
215
- if (transformed == null) {
216
- throw new Error("swc.transform returned no result");
217
- }
218
- const success = _transformresponsemodel.TransformResponse.Success({
219
- file,
220
- code: transformed.code,
221
- sourceContent: content,
222
- target,
223
- fileName,
224
- outputDir,
225
- outputPath,
226
- config: swcOptions,
227
- project
228
- });
229
- onOutput?.(success);
230
- return success;
231
- } catch (error) {
232
- const failure = _transformresponsemodel.TransformResponse.Error({
233
- file,
234
- messages: [
235
- String(error)
236
- ],
237
- config: swcOptions,
238
- project
239
- });
240
- onOutput?.(failure);
241
- return failure;
242
- }
243
- }
244
173
  async onIncrementalFileEvent(evt) {
245
174
  const { file, project } = evt;
246
175
  if (evt.eventName === _io.io.WatchEvents.unlink) {
247
- this.deleteCachedOutputs(file.fullPath);
176
+ this.#outputs.delete(file.fullPath);
248
177
  return [];
249
178
  }
250
179
  if (!project.isSourceFile(file)) {
251
180
  return [];
252
181
  }
253
182
  const freshContent = await file.getContent();
254
- const cached = this.outputCache.get(file.fullPath);
183
+ const cached = this.#outputs.get(file.fullPath);
255
184
  if (cached != null && cached[0]?.sourceContent === freshContent) {
256
185
  return [];
257
186
  }
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "transpileCache", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return transpileCache;
9
+ }
10
+ });
11
+ const _nodefs = require("node:fs");
12
+ const _nodeos = /*#__PURE__*/ _interop_require_default(require("node:os"));
13
+ const _nodepath = /*#__PURE__*/ _interop_require_default(require("node:path"));
14
+ const _core = require("@swc/core");
15
+ function _interop_require_default(obj) {
16
+ return obj && obj.__esModule ? obj : {
17
+ default: obj
18
+ };
19
+ }
20
+ /**
21
+ * Where compiled code is cached on disk (#514), or null when caching is off:
22
+ *
23
+ * - `DECOHERE_CACHE=0` turns it off.
24
+ * - `DECOHERE_CACHE_DIR` puts it in that directory.
25
+ * - Otherwise `<root>/node_modules/.cache/decohere`, where tools keep caches, or when the
26
+ * workspace has no `node_modules`, `$XDG_CACHE_HOME/decohere`, then the OS temp directory.
27
+ *
28
+ * Flux keeps its entries in the `flux` folder inside it; `forge clean` removes the folder.
29
+ */ function directory(rootDir, env = process.env) {
30
+ if (env.DECOHERE_CACHE === "0") return null;
31
+ if (env.DECOHERE_CACHE_DIR != null && env.DECOHERE_CACHE_DIR !== "") {
32
+ return _nodepath.default.resolve(env.DECOHERE_CACHE_DIR);
33
+ }
34
+ const nodeModules = _nodepath.default.join(rootDir, "node_modules");
35
+ if ((0, _nodefs.existsSync)(nodeModules)) return _nodepath.default.join(nodeModules, ".cache", "decohere");
36
+ if (env.XDG_CACHE_HOME != null && env.XDG_CACHE_HOME !== "") {
37
+ return _nodepath.default.join(env.XDG_CACHE_HOME, "decohere");
38
+ }
39
+ return _nodepath.default.join(_nodeos.default.tmpdir(), "decohere");
40
+ }
41
+ const transpileCache = {
42
+ directory,
43
+ swcVersion: _core.version
44
+ };