@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.
- package/README.md +2 -0
- package/commonjs/src/actions/index.js +1 -0
- package/commonjs/src/actions/paths.action.js +109 -0
- package/commonjs/src/commands/index.js +1 -0
- package/commonjs/src/commands/paths.command.js +24 -0
- package/commonjs/src/extensions/project.extensions.js +1 -1
- package/commonjs/src/factories/swc-config.factory.js +10 -1
- package/commonjs/src/forge.module.js +2 -0
- package/commonjs/src/index.js +4 -0
- package/commonjs/src/packaging/release-exports.js +4 -2
- package/commonjs/src/services/file-writer.service.js +0 -7
- package/commonjs/src/services/incremental-watch.js +24 -0
- package/commonjs/src/services/output-cache.js +39 -0
- package/commonjs/src/services/swc-transpiler.js +42 -113
- package/commonjs/src/services/transpile-cache.js +44 -0
- package/commonjs/src/services/type-emitter.js +13 -6
- package/commonjs/src/types/transform-response.model.js +3 -16
- package/commonjs/src/vitest/forge-build.plugin.js +25 -40
- package/es6/src/actions/index.js +1 -0
- package/es6/src/actions/paths.action.js +86 -0
- package/es6/src/commands/index.js +1 -0
- package/es6/src/commands/paths.command.js +14 -0
- package/es6/src/extensions/project.extensions.js +1 -1
- package/es6/src/factories/swc-config.factory.js +10 -1
- package/es6/src/forge.module.js +2 -0
- package/es6/src/index.js +1 -0
- package/es6/src/packaging/release-exports.js +4 -2
- package/es6/src/services/file-writer.service.js +0 -7
- package/es6/src/services/incremental-watch.js +18 -0
- package/es6/src/services/output-cache.js +29 -0
- package/es6/src/services/swc-transpiler.js +42 -113
- package/es6/src/services/transpile-cache.js +29 -0
- package/es6/src/services/type-emitter.js +13 -6
- package/es6/src/types/transform-response.model.js +0 -5
- package/es6/src/vitest/forge-build.plugin.js +26 -41
- package/package.json +5 -5
- package/types/src/actions/index.d.ts +1 -0
- package/types/src/actions/paths.action.d.ts +28 -0
- package/types/src/commands/index.d.ts +1 -0
- package/types/src/commands/paths.command.d.ts +6 -0
- package/types/src/factories/swc-config.factory.d.ts +1 -1
- package/types/src/forge.module.d.ts +2 -0
- package/types/src/index.d.ts +1 -0
- package/types/src/packaging/release-exports.d.ts +2 -2
- package/types/src/services/file-writer.service.d.ts +0 -1
- package/types/src/services/incremental-watch.d.ts +13 -0
- package/types/src/services/output-cache.d.ts +10 -0
- package/types/src/services/swc-transpiler.d.ts +14 -20
- package/types/src/services/transpile-cache.d.ts +17 -0
- package/types/src/services/type-emitter.d.ts +6 -7
- package/types/src/types/transform-response.model.d.ts +0 -5
- package/types/src/types/transformer.type.d.ts +0 -9
- package/types/src/vitest/forge-build.plugin.d.ts +0 -2
- package/commonjs/src/mixins/caches-outputs.mixin.js +0 -42
- package/commonjs/src/mixins/incremental-transform.mixin.js +0 -34
- package/es6/src/mixins/caches-outputs.mixin.js +0 -32
- package/es6/src/mixins/incremental-transform.mixin.js +0 -24
- package/types/src/mixins/caches-outputs.mixin.d.ts +0 -7
- package/types/src/mixins/incremental-transform.mixin.d.ts +0 -13
|
@@ -24,11 +24,10 @@ _export(exports, {
|
|
|
24
24
|
});
|
|
25
25
|
const _caching = require("@decohere/nexus/caching");
|
|
26
26
|
const _diagnostics = require("@decohere/nexus/diagnostics");
|
|
27
|
-
const _extensibility = require("@decohere/nexus/extensibility");
|
|
28
27
|
const _io = require("@decohere/nexus/io");
|
|
29
28
|
require("../extensions/project.extensions.js");
|
|
30
|
-
const
|
|
31
|
-
const
|
|
29
|
+
const _incrementalwatch = require("./incremental-watch.js");
|
|
30
|
+
const _outputcache = require("./output-cache.js");
|
|
32
31
|
const _tsprogramhost = require("./ts-program-host.js");
|
|
33
32
|
const _typeemissionprogram = require("./type-emission-program.js");
|
|
34
33
|
const _buildtargettype = require("../types/build-target.type.js");
|
|
@@ -55,12 +54,20 @@ const REQUIRED_TYPE_EMITTER_OPTIONS = {
|
|
|
55
54
|
const REQUIRED_TYPE_CHECK_OPTIONS = {
|
|
56
55
|
noEmit: true
|
|
57
56
|
};
|
|
58
|
-
class TypeEmitter
|
|
57
|
+
class TypeEmitter {
|
|
59
58
|
buildTargets = [
|
|
60
59
|
_buildtargettype.BuildTargets.types
|
|
61
60
|
];
|
|
62
61
|
/** Prepared programs from init(), consumed once by the next transformProject() call. */ #programs;
|
|
63
62
|
#host = new _tsprogramhost.TsProgramHost();
|
|
63
|
+
#outputs = new _outputcache.OutputCache();
|
|
64
|
+
#incremental = new _incrementalwatch.IncrementalWatch();
|
|
65
|
+
get incrementalTransform$() {
|
|
66
|
+
return this.#incremental.results$;
|
|
67
|
+
}
|
|
68
|
+
watch(workspace) {
|
|
69
|
+
this.#incremental.watch(workspace, (evt)=>this.onIncrementalFileEvent(evt));
|
|
70
|
+
}
|
|
64
71
|
/**
|
|
65
72
|
* Phase 1: fetch files, create TypeScript programs sequentially.
|
|
66
73
|
* Called before transformProject() so that the heavy createProgram work
|
|
@@ -79,7 +86,7 @@ class TypeEmitter extends (0, _extensibility.mixwith)(_cachesoutputsmixin.Caches
|
|
|
79
86
|
return [];
|
|
80
87
|
}
|
|
81
88
|
const responses = (await Promise.all(programs.map((program)=>program.emit(filter, onOutput)))).flat();
|
|
82
|
-
this.
|
|
89
|
+
this.#outputs.record(responses);
|
|
83
90
|
return responses;
|
|
84
91
|
}
|
|
85
92
|
/**
|
|
@@ -133,7 +140,7 @@ class TypeEmitter extends (0, _extensibility.mixwith)(_cachesoutputsmixin.Caches
|
|
|
133
140
|
async onIncrementalFileEvent(evt) {
|
|
134
141
|
const { file, project } = evt;
|
|
135
142
|
if (evt.eventName === _io.io.WatchEvents.unlink) {
|
|
136
|
-
this.
|
|
143
|
+
this.#outputs.delete(file.fullPath);
|
|
137
144
|
return [];
|
|
138
145
|
}
|
|
139
146
|
if (!project.isSourceFile(file)) {
|
|
@@ -2,25 +2,12 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", {
|
|
3
3
|
value: true
|
|
4
4
|
});
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
9
|
-
});
|
|
10
|
-
}
|
|
11
|
-
_export(exports, {
|
|
12
|
-
get TransformOutcomes () {
|
|
13
|
-
return TransformOutcomes;
|
|
14
|
-
},
|
|
15
|
-
get TransformResponse () {
|
|
5
|
+
Object.defineProperty(exports, "TransformResponse", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
16
8
|
return TransformResponse;
|
|
17
9
|
}
|
|
18
10
|
});
|
|
19
|
-
const TransformOutcomes = {
|
|
20
|
-
success: "success",
|
|
21
|
-
failure: "failure",
|
|
22
|
-
skipped: "skipped"
|
|
23
|
-
};
|
|
24
11
|
class TransformResponse {
|
|
25
12
|
static Success(data) {
|
|
26
13
|
return new TransformResponse({
|
|
@@ -24,11 +24,6 @@ function _interop_require_default(obj) {
|
|
|
24
24
|
// Above fatal (60) so all standard levels are nooped when logLevel is "silent"
|
|
25
25
|
const SILENT_LEVEL = 999;
|
|
26
26
|
const PLUGIN_NAME = "forge-vitest-source";
|
|
27
|
-
const TS_EXTENSIONS = [
|
|
28
|
-
".ts",
|
|
29
|
-
".tsx",
|
|
30
|
-
".mts"
|
|
31
|
-
];
|
|
32
27
|
function forgeBuild(options) {
|
|
33
28
|
const buildConfig = options?.buildConfig ?? "debug";
|
|
34
29
|
const workspaceRoot = options?.workspaceRoot ?? process.cwd();
|
|
@@ -43,9 +38,11 @@ function forgeBuild(options) {
|
|
|
43
38
|
}
|
|
44
39
|
});
|
|
45
40
|
const logger = loggerFactory(PLUGIN_NAME);
|
|
46
|
-
const skipBuild = options?.skipBuild ?? false;
|
|
47
41
|
const transpiler = new _swctranspiler.SwcTranspiler();
|
|
48
|
-
|
|
42
|
+
/**
|
|
43
|
+
* SWC options per project (tsconfig, `.swcrc`, path aliases), built once: rebuilding them for
|
|
44
|
+
* every file was most of the plugin's per-file cost, as it was in flux (#513, #517).
|
|
45
|
+
*/ const baseConfigs = new Map();
|
|
49
46
|
const transformCache = new Map();
|
|
50
47
|
let workspace = null;
|
|
51
48
|
const initWorkspace = async ()=>{
|
|
@@ -56,10 +53,17 @@ function forgeBuild(options) {
|
|
|
56
53
|
rootDir: workspaceRoot
|
|
57
54
|
});
|
|
58
55
|
};
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
56
|
+
const baseConfigFor = (project)=>{
|
|
57
|
+
const cached = baseConfigs.get(project);
|
|
58
|
+
if (cached != null) return cached;
|
|
59
|
+
const config = transpiler.createBaseConfig(_buildtargettype.BuildTargets.es6, {
|
|
60
|
+
aliasPaths: true,
|
|
61
|
+
tsConfig: project?.getTsConfigSync(),
|
|
62
|
+
userConfig: project?.getSwcConfigSync(),
|
|
63
|
+
workspace: workspace ?? undefined
|
|
64
|
+
});
|
|
65
|
+
baseConfigs.set(project, config);
|
|
66
|
+
return config;
|
|
63
67
|
};
|
|
64
68
|
return {
|
|
65
69
|
name: PLUGIN_NAME,
|
|
@@ -68,51 +72,32 @@ function forgeBuild(options) {
|
|
|
68
72
|
logger.debug("Plugin loaded");
|
|
69
73
|
},
|
|
70
74
|
resolveId (source, importer) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
return candidates.find((c)=>(0, _nodefs.existsSync)(c)) ?? null;
|
|
75
|
+
// `./a.js` is `a.ts` or `a.tsx`, `./a.mjs` is `a.mts`, `./a.cjs` is `a.cts` (#518).
|
|
76
|
+
if (importer != null && /\.[mc]?jsx?$/.test(source) && source.startsWith(".")) {
|
|
77
|
+
const written = _nodepath.default.resolve(_nodepath.default.dirname(importer), source);
|
|
78
|
+
return _mesh.sourceExtensions.candidates(written).find((c)=>(0, _nodefs.existsSync)(c)) ?? null;
|
|
76
79
|
}
|
|
77
80
|
return null;
|
|
78
81
|
},
|
|
79
82
|
watchChange (id) {
|
|
80
|
-
const file = new _io.FileEx({
|
|
81
|
-
fullPath: id
|
|
82
|
-
});
|
|
83
83
|
transformCache.delete(id);
|
|
84
|
-
transpiler.invalidateContentCache(file);
|
|
85
84
|
},
|
|
86
85
|
async transform (code, id) {
|
|
87
86
|
if (id.includes("node_modules") || !/[.](ts|tsx|mts|cts)$/.test(id)) {
|
|
88
87
|
return null;
|
|
89
88
|
}
|
|
90
|
-
await initWorkspace();
|
|
91
|
-
const project = await workspace?.findProject(id, buildConfig);
|
|
92
|
-
if (project != null && !skipBuild) {
|
|
93
|
-
preWarmProject(project);
|
|
94
|
-
}
|
|
95
89
|
const cached = transformCache.get(id);
|
|
96
90
|
if (cached !== undefined && cached.content === code) {
|
|
97
91
|
return cached.result;
|
|
98
92
|
}
|
|
99
|
-
|
|
93
|
+
await initWorkspace();
|
|
94
|
+
const project = await workspace?.findProject(id, buildConfig);
|
|
95
|
+
// The code Vite passes, not a read of the file: no content cache can serve an older
|
|
96
|
+
// version, and there is no pre-warm compiling files ahead from disk (#517).
|
|
97
|
+
const response = await transpiler.transformCode(code, new _io.FileEx({
|
|
100
98
|
fullPath: id,
|
|
101
99
|
content: code
|
|
102
|
-
});
|
|
103
|
-
if (cached !== undefined) {
|
|
104
|
-
// Content changed for a previously-seen file — invalidate the transpiler's
|
|
105
|
-
// FILE_CONTENT cache so the fresh content from Vite is used.
|
|
106
|
-
// The VS Code Vitest extension disables Vite's chokidar watcher
|
|
107
|
-
// (server.watch: null) and drives re-runs via vitest.watcher.onFileChange(),
|
|
108
|
-
// bypassing watchChange entirely — so we cannot rely on watchChange alone.
|
|
109
|
-
transpiler.invalidateContentCache(file);
|
|
110
|
-
}
|
|
111
|
-
const [response] = await transpiler.transformFile(file, _buildtargettype.BuildTargets.es6, {
|
|
112
|
-
aliasPaths: true,
|
|
113
|
-
tsConfig: project ? await project.getTsConfig() : undefined,
|
|
114
|
-
workspace: workspace ?? undefined
|
|
115
|
-
});
|
|
100
|
+
}), _buildtargettype.BuildTargets.es6, baseConfigFor(project));
|
|
116
101
|
if (!response || response.outcome !== "success") return null;
|
|
117
102
|
const result = {
|
|
118
103
|
code: response.code,
|
package/es6/src/actions/index.js
CHANGED
|
@@ -7,6 +7,7 @@ export * from "./dependency-sync.action.js";
|
|
|
7
7
|
export * from "./docker.action.js";
|
|
8
8
|
export * from "./link.action.js";
|
|
9
9
|
export * from "./package.action.js";
|
|
10
|
+
export * from "./paths.action.js";
|
|
10
11
|
export * from "./publish.action.js";
|
|
11
12
|
export * from "./sync-lock-file.action.js";
|
|
12
13
|
export * from "./tag.action.js";
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
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;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
}
|
|
7
|
+
function _ts_metadata(k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
}
|
|
10
|
+
import { existsSync } from "node:fs";
|
|
11
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
12
|
+
import path from "node:path";
|
|
13
|
+
import { injectable } from "@decohere/crucible/decorators";
|
|
14
|
+
import { packageAliases, Workspace } from "@decohere/mesh";
|
|
15
|
+
import { ActionResponse } from "@decohere/nexus/cli";
|
|
16
|
+
import { scribe } from "@decohere/scribe";
|
|
17
|
+
export const PATHS_FILE = "tsconfig.paths.json";
|
|
18
|
+
export class Paths {
|
|
19
|
+
workspace;
|
|
20
|
+
#logger = scribe.logger(Paths.name);
|
|
21
|
+
constructor(workspace){
|
|
22
|
+
this.workspace = workspace;
|
|
23
|
+
}
|
|
24
|
+
async run(options) {
|
|
25
|
+
const file = path.join(this.workspace.rootDir, PATHS_FILE);
|
|
26
|
+
const content = Paths.render(this.workspace);
|
|
27
|
+
const current = existsSync(file) ? await readFile(file, "utf8") : null;
|
|
28
|
+
const changed = current !== content;
|
|
29
|
+
if (options.check) {
|
|
30
|
+
if (!changed) {
|
|
31
|
+
return ActionResponse.Success(Paths.name, [
|
|
32
|
+
{
|
|
33
|
+
file,
|
|
34
|
+
changed,
|
|
35
|
+
succeeded: true
|
|
36
|
+
}
|
|
37
|
+
]);
|
|
38
|
+
}
|
|
39
|
+
const error = `${PATHS_FILE} is out of date. Run \`forge paths\` to update it.`;
|
|
40
|
+
this.#logger.error(error);
|
|
41
|
+
return ActionResponse.Error(Paths.name, [
|
|
42
|
+
{
|
|
43
|
+
file,
|
|
44
|
+
changed,
|
|
45
|
+
succeeded: false,
|
|
46
|
+
error
|
|
47
|
+
}
|
|
48
|
+
]);
|
|
49
|
+
}
|
|
50
|
+
if (changed) {
|
|
51
|
+
await writeFile(file, content);
|
|
52
|
+
this.#logger.info(`Wrote ${PATHS_FILE}`);
|
|
53
|
+
}
|
|
54
|
+
return ActionResponse.Success(Paths.name, [
|
|
55
|
+
{
|
|
56
|
+
file,
|
|
57
|
+
changed,
|
|
58
|
+
succeeded: true
|
|
59
|
+
}
|
|
60
|
+
]);
|
|
61
|
+
}
|
|
62
|
+
/** The file's content: convention and exported subpaths only, never the tsconfig's own paths. */ static render(workspace) {
|
|
63
|
+
const rootDir = path.resolve(workspace.rootDir);
|
|
64
|
+
const { paths } = packageAliases.build(workspace.getProjects("debug"), {
|
|
65
|
+
baseUrl: rootDir
|
|
66
|
+
});
|
|
67
|
+
const sorted = Object.fromEntries(Object.keys(paths).sort().map((key)=>[
|
|
68
|
+
key,
|
|
69
|
+
(paths[key] ?? []).map((target)=>`./${target}`)
|
|
70
|
+
]));
|
|
71
|
+
const json = {
|
|
72
|
+
$comment: "Generated by `forge paths` from the workspace packages. Do not edit.",
|
|
73
|
+
compilerOptions: {
|
|
74
|
+
paths: sorted
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
return `${JSON.stringify(json, null, 2)}\n`;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
Paths = _ts_decorate([
|
|
81
|
+
injectable(),
|
|
82
|
+
_ts_metadata("design:type", Function),
|
|
83
|
+
_ts_metadata("design:paramtypes", [
|
|
84
|
+
typeof Workspace === "undefined" ? Object : Workspace
|
|
85
|
+
])
|
|
86
|
+
], Paths);
|
|
@@ -4,6 +4,7 @@ export * from "./docker.command.js";
|
|
|
4
4
|
export * from "./forge.command.js";
|
|
5
5
|
export * from "./link.command.js";
|
|
6
6
|
export * from "./package.command.js";
|
|
7
|
+
export * from "./paths.command.js";
|
|
7
8
|
export * from "./publish.command.js";
|
|
8
9
|
export * from "./tag.command.js";
|
|
9
10
|
export * from "./typecheck.command.js";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { DecohereCommand } from "@decohere/nexus/cli";
|
|
2
|
+
import { Paths } from "../actions/paths.action.js";
|
|
3
|
+
export class PathsCommand extends DecohereCommand {
|
|
4
|
+
get actions() {
|
|
5
|
+
return [
|
|
6
|
+
Paths
|
|
7
|
+
];
|
|
8
|
+
}
|
|
9
|
+
constructor(name){
|
|
10
|
+
super(name);
|
|
11
|
+
this.description("Write tsconfig.paths.json: tsconfig paths for the workspace packages and their exported subpaths");
|
|
12
|
+
this.option("--check", "Fail if tsconfig.paths.json is missing or out of date", false);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -58,9 +58,18 @@ export class SwcConfigFactory {
|
|
|
58
58
|
mergeArrays: false
|
|
59
59
|
}, defaultOptions, options, userConfig ?? {});
|
|
60
60
|
}
|
|
61
|
-
/** Stamps a per-file filename onto a pre-built base config. */ createFileConfig(file, baseConfig) {
|
|
61
|
+
/** Stamps a per-file filename onto a pre-built base config; `.tsx` also turns on JSX (#518). */ createFileConfig(file, baseConfig) {
|
|
62
|
+
const tsx = file.fullPath.endsWith(".tsx");
|
|
63
|
+
const jsc = tsx && baseConfig.jsc?.parser?.syntax === "typescript" ? {
|
|
64
|
+
...baseConfig.jsc,
|
|
65
|
+
parser: {
|
|
66
|
+
...baseConfig.jsc.parser,
|
|
67
|
+
tsx: true
|
|
68
|
+
}
|
|
69
|
+
} : baseConfig.jsc;
|
|
62
70
|
const config = {
|
|
63
71
|
...baseConfig,
|
|
72
|
+
jsc,
|
|
64
73
|
filename: file.fullPath
|
|
65
74
|
};
|
|
66
75
|
this.validate(config);
|
package/es6/src/forge.module.js
CHANGED
|
@@ -25,6 +25,7 @@ export class ForgeModule extends ModuleManifest {
|
|
|
25
25
|
reportDependencies: Actions.ReportDependencies,
|
|
26
26
|
version: Actions.Version,
|
|
27
27
|
tag: Actions.Tag,
|
|
28
|
+
paths: Actions.Paths,
|
|
28
29
|
typecheck: Actions.TypeCheck,
|
|
29
30
|
commitFiles: Actions.CommitFiles,
|
|
30
31
|
link: Actions.Link,
|
|
@@ -38,6 +39,7 @@ export class ForgeModule extends ModuleManifest {
|
|
|
38
39
|
package: Commands.PackageCommand,
|
|
39
40
|
version: Commands.VersionCommand,
|
|
40
41
|
tag: Commands.TagCommand,
|
|
42
|
+
paths: Commands.PathsCommand,
|
|
41
43
|
typecheck: Commands.TypeCheckCommand,
|
|
42
44
|
link: Commands.LinkCommand,
|
|
43
45
|
unlink: Commands.UnlinkCommand
|
package/es6/src/index.js
CHANGED
|
@@ -2,5 +2,6 @@ export { createWorkspace } from "./create-workspace.js";
|
|
|
2
2
|
export { ForgeError } from "./errors.js";
|
|
3
3
|
export { ForgeModule } from "./forge.module.js";
|
|
4
4
|
export { SwcTranspiler } from "./services/swc-transpiler.js";
|
|
5
|
+
export { transpileCache } from "./services/transpile-cache.js";
|
|
5
6
|
export { BuildTargets } from "./types/build-target.type.js";
|
|
6
7
|
export { Debug as DebugConfiguration, DefaultConfigurations, Link as LinkConfiguration, Release as ReleaseConfiguration, WorkspaceDefaults } from "./workspace.config.js";
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
import { sourceExtensions } from "@decohere/mesh";
|
|
1
2
|
const ES6_PREFIX = "./es6/";
|
|
2
3
|
/** Condition names that can point at runnable ESM, most specific first. `types` never does. */ const RUNTIME_CONDITIONS = [
|
|
3
4
|
"import",
|
|
4
5
|
"node",
|
|
5
6
|
"default"
|
|
6
7
|
];
|
|
7
|
-
/** `src/main.ts` becomes `src/main.js`, with any leading `./` dropped. */ function toJs(filePath) {
|
|
8
|
-
|
|
8
|
+
/** `src/main.ts` becomes `src/main.js` (`.mts` `.mjs`, `.cts` `.cjs`), with any leading `./` dropped. */ function toJs(filePath) {
|
|
9
|
+
const file = filePath.replace(/^\.\//, "");
|
|
10
|
+
return sourceExtensions.isSource(file) ? sourceExtensions.outputPath(file) : file;
|
|
9
11
|
}
|
|
10
12
|
/**
|
|
11
13
|
* The `exports` a library needs once only its `es6/` output is shipped: the library's own
|
|
@@ -14,19 +14,12 @@ export class FileWriterService {
|
|
|
14
14
|
#artifactByPath = new Map();
|
|
15
15
|
#warmupSignature = "";
|
|
16
16
|
#warmupPromise;
|
|
17
|
-
#initialized = false;
|
|
18
17
|
#pendingWrites = [];
|
|
19
18
|
#activeWorkers = 0;
|
|
20
19
|
#drainResolvers = [];
|
|
21
20
|
#ensuredDirs = new Set();
|
|
22
21
|
#ensureDirPromises = new Map();
|
|
23
22
|
#workerError;
|
|
24
|
-
async initialize() {
|
|
25
|
-
if (this.#initialized) {
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
this.#initialized = true;
|
|
29
|
-
}
|
|
30
23
|
async warmup(projects) {
|
|
31
24
|
const outputDirs = this.getOutputDirs(projects);
|
|
32
25
|
const signature = outputDirs.join("|");
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Subject } from "rxjs";
|
|
2
|
+
/**
|
|
3
|
+
* Runs a transformer's handler for every workspace file change and emits what it built. The
|
|
4
|
+
* handler invalidates the transformer's own caches, rebuilds, and returns the responses (empty
|
|
5
|
+
* for a deletion or when nothing was built).
|
|
6
|
+
*/ export class IncrementalWatch {
|
|
7
|
+
#results = new Subject();
|
|
8
|
+
get results$() {
|
|
9
|
+
return this.#results.asObservable();
|
|
10
|
+
}
|
|
11
|
+
watch(workspace, handler) {
|
|
12
|
+
workspace.projectFileChanged$.subscribe((evt)=>{
|
|
13
|
+
void handler(evt).then((responses)=>{
|
|
14
|
+
this.#results.next(responses);
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { io } from "@decohere/nexus/io";
|
|
2
|
+
/** The outputs each source file last built to, so watch mode can delete them when it goes. */ export class OutputCache {
|
|
3
|
+
#bySource = new Map();
|
|
4
|
+
get(sourcePath) {
|
|
5
|
+
return this.#bySource.get(sourcePath);
|
|
6
|
+
}
|
|
7
|
+
/** Records the successful outputs, replacing what each of their source files had before. */ record(responses) {
|
|
8
|
+
const bySource = new Map();
|
|
9
|
+
for (const response of responses){
|
|
10
|
+
if (response.outcome !== "success") continue;
|
|
11
|
+
const group = bySource.get(response.file.fullPath) ?? [];
|
|
12
|
+
group.push(response);
|
|
13
|
+
bySource.set(response.file.fullPath, group);
|
|
14
|
+
}
|
|
15
|
+
for (const [sourcePath, group] of bySource){
|
|
16
|
+
this.#bySource.set(sourcePath, group);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/** Deletes the files a source built to and forgets them. */ delete(sourcePath) {
|
|
20
|
+
const outputs = this.#bySource.get(sourcePath);
|
|
21
|
+
if (outputs == null) return;
|
|
22
|
+
for (const response of outputs){
|
|
23
|
+
if (response.outputPath != null) {
|
|
24
|
+
void io.deleteFile(response.outputPath);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
this.#bySource.delete(sourcePath);
|
|
28
|
+
}
|
|
29
|
+
}
|