@decohere/forge 0.0.1-dev.130 → 0.0.1-dev.131
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/services/incremental-watch.js +2 -2
- package/commonjs/src/services/ts-compiler-options.js +50 -0
- package/commonjs/src/services/type-emission-program.js +1 -1
- package/commonjs/src/services/type-emitter.js +9 -4
- package/commonjs/src/transpile/index.js +30 -0
- package/es6/src/services/incremental-watch.js +1 -1
- package/es6/src/services/ts-compiler-options.js +39 -0
- package/es6/src/services/type-emission-program.js +1 -1
- package/es6/src/services/type-emitter.js +9 -4
- package/es6/src/transpile/index.js +6 -0
- package/package.json +11 -7
- package/types/src/services/incremental-watch.d.ts +1 -1
- package/types/src/services/swc-transpiler.d.ts +1 -1
- package/types/src/services/ts-compiler-options.d.ts +8 -0
- package/types/src/services/type-emission-program.d.ts +2 -0
- package/types/src/services/type-emitter.d.ts +1 -1
- package/types/src/transpile/index.d.ts +6 -0
- package/types/src/types/transformer.type.d.ts +1 -1
package/README.md
CHANGED
|
@@ -6,6 +6,8 @@ Build system (SWC). `forge build --configuration debug` rewrites workspace impor
|
|
|
6
6
|
|
|
7
7
|
`forge build` transpiles without waiting for a whole-workspace typecheck. Run `forge typecheck` independently (including in the background) and require it before shipping. Why executable tests come before static contracts: [testing principles](../../docs/testing/#executable-behavior-before-type-contracts).
|
|
8
8
|
|
|
9
|
+
With no `tsconfig.json`, `forge typecheck` reads a project as plain `tsc` would, with decorators on because forge and flux compile them by default. Add a `tsconfig.json` to choose your own options; forge then limits `lib` to ES2022 and turns off `@types` discovery unless it sets them (`types: ["node"]`).
|
|
10
|
+
|
|
9
11
|
## Debug path aliases (not `node_modules` links)
|
|
10
12
|
|
|
11
13
|
Forge debug (`aliasPaths: true`) asks the workspace package 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.
|
|
@@ -8,9 +8,9 @@ Object.defineProperty(exports, "IncrementalWatch", {
|
|
|
8
8
|
return IncrementalWatch;
|
|
9
9
|
}
|
|
10
10
|
});
|
|
11
|
-
const
|
|
11
|
+
const _system = require("@decohere/core/system");
|
|
12
12
|
class IncrementalWatch {
|
|
13
|
-
#results = new
|
|
13
|
+
#results = new _system.Subject();
|
|
14
14
|
get results$() {
|
|
15
15
|
return this.#results.asObservable();
|
|
16
16
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "tsCompilerOptions", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return tsCompilerOptions;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _nodefs = /*#__PURE__*/ _interop_require_default(require("node:fs"));
|
|
12
|
+
const _nodepath = /*#__PURE__*/ _interop_require_default(require("node:path"));
|
|
13
|
+
const _typescript = /*#__PURE__*/ _interop_require_default(require("typescript"));
|
|
14
|
+
const _errors = require("../errors.js");
|
|
15
|
+
function _interop_require_default(obj) {
|
|
16
|
+
return obj && obj.__esModule ? obj : {
|
|
17
|
+
default: obj
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const host = {
|
|
21
|
+
..._typescript.default.sys,
|
|
22
|
+
readFile: (file)=>_nodefs.default.readFileSync(file, {
|
|
23
|
+
encoding: "utf8"
|
|
24
|
+
}),
|
|
25
|
+
readDirectory: (directoryName)=>_nodefs.default.readdirSync(directoryName, {
|
|
26
|
+
withFileTypes: true
|
|
27
|
+
}).filter((entry)=>entry.isDirectory()).map((entry)=>entry.name)
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* No tsconfig means no choices made, so this reads the project as plain tsc would: it clears
|
|
31
|
+
* the `lib` and `types` limits forge sets to keep monorepos fast, which a project opts out of
|
|
32
|
+
* in its tsconfig. Decorators are on because forge and flux compile them by default.
|
|
33
|
+
*/ const DEFAULT_TS_OPTIONS = {
|
|
34
|
+
emitDeclarationOnly: true,
|
|
35
|
+
declaration: true,
|
|
36
|
+
lib: undefined,
|
|
37
|
+
types: undefined,
|
|
38
|
+
experimentalDecorators: true
|
|
39
|
+
};
|
|
40
|
+
function tsCompilerOptions(tsconfig) {
|
|
41
|
+
const { filePath } = tsconfig;
|
|
42
|
+
if (filePath == null) return {
|
|
43
|
+
...DEFAULT_TS_OPTIONS
|
|
44
|
+
};
|
|
45
|
+
const result = _typescript.default.parseConfigFileTextToJson(filePath, _nodefs.default.readFileSync(filePath, "utf8"));
|
|
46
|
+
if (result.error) {
|
|
47
|
+
throw new _errors.ForgeError(`Error parsing ${filePath}. ${_typescript.default.flattenDiagnosticMessageText(result.error.messageText, "\n")}`);
|
|
48
|
+
}
|
|
49
|
+
return _typescript.default.parseJsonConfigFileContent(result.config, host, _nodepath.default.dirname(filePath)).options;
|
|
50
|
+
}
|
|
@@ -42,7 +42,7 @@ class TypeEmissionProgram {
|
|
|
42
42
|
this.#targetsByDtsPath = targets.byDtsPath;
|
|
43
43
|
this.#targetsByEmittedPath = targets.byEmittedPath;
|
|
44
44
|
this.#host = hostFactory;
|
|
45
|
-
this.#compilerOptions = group[0].
|
|
45
|
+
this.#compilerOptions = group[0].compilerOptions;
|
|
46
46
|
this.#requiredOptions = requiredOptions;
|
|
47
47
|
this.#allFiles = group.flatMap(({ files })=>files);
|
|
48
48
|
this.#compilerHost = hostFactory.createHost(this.#compilerOptions);
|
|
@@ -28,6 +28,7 @@ const _io = require("@decohere/devkit/io");
|
|
|
28
28
|
require("../extensions/project.extensions.js");
|
|
29
29
|
const _incrementalwatch = require("./incremental-watch.js");
|
|
30
30
|
const _outputcache = require("./output-cache.js");
|
|
31
|
+
const _tscompileroptions = require("./ts-compiler-options.js");
|
|
31
32
|
const _tsprogramhost = require("./ts-program-host.js");
|
|
32
33
|
const _typeemissionprogram = require("./type-emission-program.js");
|
|
33
34
|
const _buildtargettype = require("../types/build-target.type.js");
|
|
@@ -121,17 +122,21 @@ class TypeEmitter {
|
|
|
121
122
|
return [];
|
|
122
123
|
}
|
|
123
124
|
// Pre-fetch all files and tsconfigs in parallel across all projects
|
|
124
|
-
const projectData = await Promise.all(projects.map(async (proj)=>
|
|
125
|
+
const projectData = await Promise.all(projects.map(async (proj)=>{
|
|
126
|
+
const tsconfig = await proj.getTsConfig();
|
|
127
|
+
return {
|
|
125
128
|
project: proj,
|
|
126
129
|
files: await proj.getAllFiles(),
|
|
127
|
-
tsconfig
|
|
128
|
-
|
|
130
|
+
tsconfig,
|
|
131
|
+
compilerOptions: (0, _tscompileroptions.tsCompilerOptions)(tsconfig)
|
|
132
|
+
};
|
|
133
|
+
}));
|
|
129
134
|
// Group projects by the resolved compiler options that affect the compiler
|
|
130
135
|
// host (lib loading, module resolution, path aliases). Projects sharing the
|
|
131
136
|
// same resolved values get one shared host AND one shared program — all their
|
|
132
137
|
// files are compiled together, matching v1's single-program approach which
|
|
133
138
|
// avoids re-parsing lib/node_modules .d.ts files per project.
|
|
134
|
-
const groups = Map.groupBy(projectData, ({
|
|
139
|
+
const groups = Map.groupBy(projectData, ({ compilerOptions })=>this.#host.hostKey(compilerOptions));
|
|
135
140
|
const requiredCompilerOptions = options.requiredCompilerOptions ?? REQUIRED_TYPE_EMITTER_OPTIONS;
|
|
136
141
|
return [
|
|
137
142
|
...groups.values()
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// What compiling one file needs (flux, the vitest plugin), without forge's commands: those
|
|
2
|
+
// load the TypeScript compiler, which a plain `flux run` never uses.
|
|
3
|
+
"use strict";
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
function _export(target, all) {
|
|
8
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
9
|
+
enumerable: true,
|
|
10
|
+
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
_export(exports, {
|
|
14
|
+
get BuildTargets () {
|
|
15
|
+
return _buildtargettype.BuildTargets;
|
|
16
|
+
},
|
|
17
|
+
get SwcTranspiler () {
|
|
18
|
+
return _swctranspiler.SwcTranspiler;
|
|
19
|
+
},
|
|
20
|
+
get createWorkspace () {
|
|
21
|
+
return _createworkspace.createWorkspace;
|
|
22
|
+
},
|
|
23
|
+
get transpileCache () {
|
|
24
|
+
return _transpilecache.transpileCache;
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
const _createworkspace = require("../create-workspace.js");
|
|
28
|
+
const _swctranspiler = require("../services/swc-transpiler.js");
|
|
29
|
+
const _transpilecache = require("../services/transpile-cache.js");
|
|
30
|
+
const _buildtargettype = require("../types/build-target.type.js");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Subject } from "
|
|
1
|
+
import { Subject } from "@decohere/core/system";
|
|
2
2
|
/**
|
|
3
3
|
* Runs a transformer's handler for every workspace file change and emits what it built. The
|
|
4
4
|
* handler invalidates the transformer's own caches, rebuilds, and returns the responses (empty
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import ts from "typescript";
|
|
4
|
+
import { ForgeError } from "../errors.js";
|
|
5
|
+
const host = {
|
|
6
|
+
...ts.sys,
|
|
7
|
+
readFile: (file)=>fs.readFileSync(file, {
|
|
8
|
+
encoding: "utf8"
|
|
9
|
+
}),
|
|
10
|
+
readDirectory: (directoryName)=>fs.readdirSync(directoryName, {
|
|
11
|
+
withFileTypes: true
|
|
12
|
+
}).filter((entry)=>entry.isDirectory()).map((entry)=>entry.name)
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* No tsconfig means no choices made, so this reads the project as plain tsc would: it clears
|
|
16
|
+
* the `lib` and `types` limits forge sets to keep monorepos fast, which a project opts out of
|
|
17
|
+
* in its tsconfig. Decorators are on because forge and flux compile them by default.
|
|
18
|
+
*/ const DEFAULT_TS_OPTIONS = {
|
|
19
|
+
emitDeclarationOnly: true,
|
|
20
|
+
declaration: true,
|
|
21
|
+
lib: undefined,
|
|
22
|
+
types: undefined,
|
|
23
|
+
experimentalDecorators: true
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* A project's compiler options as the TypeScript compiler parses its `tsconfig.json`: what
|
|
27
|
+
* type emission needs (enums, every option, tsc's own defaults). The workspace reads only
|
|
28
|
+
* what transpiling needs, without loading the compiler.
|
|
29
|
+
*/ export function tsCompilerOptions(tsconfig) {
|
|
30
|
+
const { filePath } = tsconfig;
|
|
31
|
+
if (filePath == null) return {
|
|
32
|
+
...DEFAULT_TS_OPTIONS
|
|
33
|
+
};
|
|
34
|
+
const result = ts.parseConfigFileTextToJson(filePath, fs.readFileSync(filePath, "utf8"));
|
|
35
|
+
if (result.error) {
|
|
36
|
+
throw new ForgeError(`Error parsing ${filePath}. ${ts.flattenDiagnosticMessageText(result.error.messageText, "\n")}`);
|
|
37
|
+
}
|
|
38
|
+
return ts.parseJsonConfigFileContent(result.config, host, path.dirname(filePath)).options;
|
|
39
|
+
}
|
|
@@ -27,7 +27,7 @@ export class TypeEmissionProgram {
|
|
|
27
27
|
this.#targetsByDtsPath = targets.byDtsPath;
|
|
28
28
|
this.#targetsByEmittedPath = targets.byEmittedPath;
|
|
29
29
|
this.#host = hostFactory;
|
|
30
|
-
this.#compilerOptions = group[0].
|
|
30
|
+
this.#compilerOptions = group[0].compilerOptions;
|
|
31
31
|
this.#requiredOptions = requiredOptions;
|
|
32
32
|
this.#allFiles = group.flatMap(({ files })=>files);
|
|
33
33
|
this.#compilerHost = hostFactory.createHost(this.#compilerOptions);
|
|
@@ -13,6 +13,7 @@ import { WatchEvents } from "@decohere/devkit/io";
|
|
|
13
13
|
import "../extensions/project.extensions.js";
|
|
14
14
|
import { IncrementalWatch } from "./incremental-watch.js";
|
|
15
15
|
import { OutputCache } from "./output-cache.js";
|
|
16
|
+
import { tsCompilerOptions } from "./ts-compiler-options.js";
|
|
16
17
|
import { TsProgramHost } from "./ts-program-host.js";
|
|
17
18
|
import { TypeEmissionProgram } from "./type-emission-program.js";
|
|
18
19
|
import { BuildTargets } from "../types/build-target.type.js";
|
|
@@ -111,17 +112,21 @@ export class TypeEmitter {
|
|
|
111
112
|
return [];
|
|
112
113
|
}
|
|
113
114
|
// Pre-fetch all files and tsconfigs in parallel across all projects
|
|
114
|
-
const projectData = await Promise.all(projects.map(async (proj)=>
|
|
115
|
+
const projectData = await Promise.all(projects.map(async (proj)=>{
|
|
116
|
+
const tsconfig = await proj.getTsConfig();
|
|
117
|
+
return {
|
|
115
118
|
project: proj,
|
|
116
119
|
files: await proj.getAllFiles(),
|
|
117
|
-
tsconfig
|
|
118
|
-
|
|
120
|
+
tsconfig,
|
|
121
|
+
compilerOptions: tsCompilerOptions(tsconfig)
|
|
122
|
+
};
|
|
123
|
+
}));
|
|
119
124
|
// Group projects by the resolved compiler options that affect the compiler
|
|
120
125
|
// host (lib loading, module resolution, path aliases). Projects sharing the
|
|
121
126
|
// same resolved values get one shared host AND one shared program — all their
|
|
122
127
|
// files are compiled together, matching v1's single-program approach which
|
|
123
128
|
// avoids re-parsing lib/node_modules .d.ts files per project.
|
|
124
|
-
const groups = Map.groupBy(projectData, ({
|
|
129
|
+
const groups = Map.groupBy(projectData, ({ compilerOptions })=>this.#host.hostKey(compilerOptions));
|
|
125
130
|
const requiredCompilerOptions = options.requiredCompilerOptions ?? REQUIRED_TYPE_EMITTER_OPTIONS;
|
|
126
131
|
return [
|
|
127
132
|
...groups.values()
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// What compiling one file needs (flux, the vitest plugin), without forge's commands: those
|
|
2
|
+
// load the TypeScript compiler, which a plain `flux run` never uses.
|
|
3
|
+
export { createWorkspace } from "../create-workspace.js";
|
|
4
|
+
export { SwcTranspiler } from "../services/swc-transpiler.js";
|
|
5
|
+
export { transpileCache } from "../services/transpile-cache.js";
|
|
6
|
+
export { BuildTargets } from "../types/build-target.type.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decohere/forge",
|
|
3
|
-
"version": "0.0.1-dev.
|
|
3
|
+
"version": "0.0.1-dev.131",
|
|
4
4
|
"description": "TypeScript monorepo build system — zero config, fast compilation, streamlined developer workflow",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -28,6 +28,11 @@
|
|
|
28
28
|
"import": "./es6/src/index.js",
|
|
29
29
|
"require": "./commonjs/src/index.js"
|
|
30
30
|
},
|
|
31
|
+
"./transpile": {
|
|
32
|
+
"types": "./types/src/transpile/index.d.ts",
|
|
33
|
+
"import": "./es6/src/transpile/index.js",
|
|
34
|
+
"require": "./commonjs/src/transpile/index.js"
|
|
35
|
+
},
|
|
31
36
|
"./vitest": {
|
|
32
37
|
"types": "./types/src/vitest/index.d.ts",
|
|
33
38
|
"import": "./es6/src/vitest/index.js",
|
|
@@ -38,15 +43,14 @@
|
|
|
38
43
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
39
44
|
},
|
|
40
45
|
"dependencies": {
|
|
41
|
-
"@decohere/core": "0.0.1-dev.
|
|
42
|
-
"@decohere/crucible": "0.0.1-dev.
|
|
43
|
-
"@decohere/devkit": "0.0.1-dev.
|
|
44
|
-
"@decohere/scribe": "0.0.1-dev.
|
|
45
|
-
"@decohere/workspace": "0.0.1-dev.
|
|
46
|
+
"@decohere/core": "0.0.1-dev.131",
|
|
47
|
+
"@decohere/crucible": "0.0.1-dev.131",
|
|
48
|
+
"@decohere/devkit": "0.0.1-dev.131",
|
|
49
|
+
"@decohere/scribe": "0.0.1-dev.131",
|
|
50
|
+
"@decohere/workspace": "0.0.1-dev.131",
|
|
46
51
|
"@swc/core": "^1.11.11",
|
|
47
52
|
"async": "^3.2.6",
|
|
48
53
|
"parse-imports": "^2.2.1",
|
|
49
|
-
"rxjs": "^7.8.2",
|
|
50
54
|
"typescript": "^6.0.3"
|
|
51
55
|
},
|
|
52
56
|
"devDependencies": {}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ProjectFileEvent, Workspace } from "@decohere/workspace";
|
|
2
|
-
import type { Observable } from "
|
|
2
|
+
import type { Observable } from "@decohere/core/system";
|
|
3
3
|
import type { TransformResponse } from "../types/transform-response.model.js";
|
|
4
4
|
/**
|
|
5
5
|
* Runs a transformer's handler for every workspace file change and emits what it built. The
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type Project, type Tsconfig, type Workspace } from "@decohere/workspace";
|
|
2
2
|
import type { FileEx } from "@decohere/core/io";
|
|
3
3
|
import { type Options } from "@swc/core";
|
|
4
|
-
import type { Observable } from "
|
|
4
|
+
import type { Observable } from "@decohere/core/system";
|
|
5
5
|
import { type BuildTarget, type TranspileTarget } from "../types/build-target.type.js";
|
|
6
6
|
import "../extensions/project.extensions.js";
|
|
7
7
|
import type { SwcConfig } from "../types/swc-config.type.js";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Tsconfig } from "@decohere/workspace";
|
|
2
|
+
import ts from "typescript";
|
|
3
|
+
/**
|
|
4
|
+
* A project's compiler options as the TypeScript compiler parses its `tsconfig.json`: what
|
|
5
|
+
* type emission needs (enums, every option, tsc's own defaults). The workspace reads only
|
|
6
|
+
* what transpiling needs, without loading the compiler.
|
|
7
|
+
*/
|
|
8
|
+
export declare function tsCompilerOptions(tsconfig: Tsconfig): ts.CompilerOptions;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Project, Workspace } from "@decohere/workspace";
|
|
2
2
|
import type { FileEx } from "@decohere/core/io";
|
|
3
|
-
import type { Observable } from "
|
|
3
|
+
import type { Observable } from "@decohere/core/system";
|
|
4
4
|
import type ts from "typescript";
|
|
5
5
|
import "../extensions/project.extensions.js";
|
|
6
6
|
import { type BuildTarget } from "../types/build-target.type.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { createWorkspace } from "../create-workspace.js";
|
|
2
|
+
export type { TranspileOptions } from "../services/swc-transpiler.js";
|
|
3
|
+
export { SwcTranspiler } from "../services/swc-transpiler.js";
|
|
4
|
+
export { transpileCache } from "../services/transpile-cache.js";
|
|
5
|
+
export type { BuildTarget, TranspileTarget } from "../types/build-target.type.js";
|
|
6
|
+
export { BuildTargets } from "../types/build-target.type.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Project, Workspace } from "@decohere/workspace";
|
|
2
2
|
import type { FileEx } from "@decohere/core/io";
|
|
3
|
-
import type { Observable } from "
|
|
3
|
+
import type { Observable } from "@decohere/core/system";
|
|
4
4
|
import type { BuildTarget } from "../types/build-target.type.js";
|
|
5
5
|
import type { TransformResponse } from "./transform-response.model.js";
|
|
6
6
|
export declare const TransformerKey: unique symbol;
|