@decohere/workspace 0.0.1-dev.125 → 0.0.1-dev.5

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 (75) hide show
  1. package/package.json +3 -13
  2. package/src/const.ts +19 -0
  3. package/src/errors.ts +1 -0
  4. package/src/factories/config.factory.ts +21 -0
  5. package/src/factories/find-workspace-root.ts +32 -0
  6. package/src/factories/project.factory.ts +75 -0
  7. package/src/factories/workspace.factory.ts +44 -0
  8. package/{types/src/index.d.ts → src/index.ts} +7 -1
  9. package/src/models/has-package-json.mixin.ts +30 -0
  10. package/src/models/has-tsconfig.mixin.ts +48 -0
  11. package/src/models/package-aliases.ts +130 -0
  12. package/src/models/package-json.model.ts +231 -0
  13. package/src/models/project-file-event.ts +9 -0
  14. package/src/models/project.model.ts +340 -0
  15. package/src/models/source-extensions.ts +59 -0
  16. package/src/models/tsconfig.model.ts +81 -0
  17. package/src/models/workspace-config.type.ts +57 -0
  18. package/src/models/workspace.model.ts +178 -0
  19. package/tests/find-workspace-root.spec.ts +30 -0
  20. package/tests/package-aliases.spec.ts +160 -0
  21. package/tests/package-json.model.spec.ts +78 -0
  22. package/tests/project.factory.spec.ts +100 -0
  23. package/tests/source-extensions.spec.ts +33 -0
  24. package/tests/workspace.factory.spec.ts +147 -0
  25. package/LICENSE +0 -15
  26. package/commonjs/src/const.js +0 -69
  27. package/commonjs/src/errors.js +0 -12
  28. package/commonjs/src/factories/config.factory.js +0 -90
  29. package/commonjs/src/factories/find-workspace-root.js +0 -37
  30. package/commonjs/src/factories/project.factory.js +0 -73
  31. package/commonjs/src/factories/workspace.factory.js +0 -50
  32. package/commonjs/src/index.js +0 -74
  33. package/commonjs/src/models/has-package-json.mixin.js +0 -50
  34. package/commonjs/src/models/has-tsconfig.mixin.js +0 -75
  35. package/commonjs/src/models/package-aliases.js +0 -112
  36. package/commonjs/src/models/package-json.model.js +0 -202
  37. package/commonjs/src/models/project-file-event.js +0 -4
  38. package/commonjs/src/models/project.model.js +0 -316
  39. package/commonjs/src/models/source-extensions.js +0 -81
  40. package/commonjs/src/models/tsconfig.model.js +0 -97
  41. package/commonjs/src/models/workspace-config.type.js +0 -7
  42. package/commonjs/src/models/workspace.model.js +0 -153
  43. package/es6/src/const.js +0 -36
  44. package/es6/src/errors.js +0 -2
  45. package/es6/src/factories/config.factory.js +0 -34
  46. package/es6/src/factories/find-workspace-root.js +0 -26
  47. package/es6/src/factories/project.factory.js +0 -58
  48. package/es6/src/factories/workspace.factory.js +0 -35
  49. package/es6/src/index.js +0 -14
  50. package/es6/src/models/has-package-json.mixin.js +0 -35
  51. package/es6/src/models/has-tsconfig.mixin.js +0 -60
  52. package/es6/src/models/package-aliases.js +0 -97
  53. package/es6/src/models/package-json.model.js +0 -192
  54. package/es6/src/models/project-file-event.js +0 -1
  55. package/es6/src/models/project.model.js +0 -301
  56. package/es6/src/models/source-extensions.js +0 -66
  57. package/es6/src/models/tsconfig.model.js +0 -74
  58. package/es6/src/models/workspace-config.type.js +0 -4
  59. package/es6/src/models/workspace.model.js +0 -143
  60. package/types/src/const.d.ts +0 -8
  61. package/types/src/errors.d.ts +0 -2
  62. package/types/src/factories/config.factory.d.ts +0 -6
  63. package/types/src/factories/find-workspace-root.d.ts +0 -6
  64. package/types/src/factories/project.factory.d.ts +0 -8
  65. package/types/src/factories/workspace.factory.d.ts +0 -9
  66. package/types/src/models/has-package-json.mixin.d.ts +0 -9
  67. package/types/src/models/has-tsconfig.mixin.d.ts +0 -11
  68. package/types/src/models/package-aliases.d.ts +0 -28
  69. package/types/src/models/package-json.model.d.ts +0 -40
  70. package/types/src/models/project-file-event.d.ts +0 -9
  71. package/types/src/models/project.model.d.ts +0 -64
  72. package/types/src/models/source-extensions.d.ts +0 -22
  73. package/types/src/models/tsconfig.model.d.ts +0 -11
  74. package/types/src/models/workspace-config.type.d.ts +0 -52
  75. package/types/src/models/workspace.model.d.ts +0 -40
@@ -1,301 +0,0 @@
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 path from "node:path";
12
- import { cache, memoize } from "@decohere/core/caching";
13
- import { mixwith } from "@decohere/core/extensibility";
14
- import { findFiles, WatchEvents, watch as watchDirectory } from "@decohere/devkit/io";
15
- import picomatch from "picomatch";
16
- import { Subject } from "rxjs";
17
- import { WorkspaceConfigurationError } from "../errors.js";
18
- import { HasPackageJson } from "./has-package-json.mixin.js";
19
- import { HasTsConfig } from "./has-tsconfig.mixin.js";
20
- import { PackageJson } from "./package-json.model.js";
21
- import { sourceExtensions } from "./source-extensions.js";
22
- export class Project extends mixwith(HasPackageJson, HasTsConfig) {
23
- packageJson;
24
- #projectFileChangedSubject = new Subject();
25
- #sourceFiles = new Map();
26
- #projectConfig;
27
- #name;
28
- #projectDirectory;
29
- #selectedConfiguration;
30
- #hasLoadedSourceFiles = false;
31
- #watcher;
32
- constructor(name, config = {}, selectedConfigurationName){
33
- super();
34
- this.#name = name;
35
- this.#projectDirectory = config?.projectDir ?? name;
36
- this.#projectConfig = config;
37
- this.#selectedConfiguration = this.resolveConfiguration(selectedConfigurationName);
38
- this.packageJson = config.packageJson ?? new PackageJson();
39
- }
40
- get name() {
41
- return this.#name;
42
- }
43
- get isPrivate() {
44
- return this.packageJson?.private === true;
45
- }
46
- toString() {
47
- return `Project(${this.#name})`;
48
- }
49
- get projectDirectory() {
50
- return this.#projectDirectory;
51
- }
52
- get projectRoot() {
53
- return path.resolve(this.#projectConfig.rootDir ?? "./", this.#projectDirectory);
54
- }
55
- get configuration() {
56
- return this.#selectedConfiguration;
57
- }
58
- get packageJsonDirectory() {
59
- return this.#absoluteProjectDir;
60
- }
61
- get tsConfigDirectory() {
62
- return this.#absoluteProjectDir;
63
- }
64
- get fallbackTsConfigDirectory() {
65
- return this.#projectConfig.rootDir;
66
- }
67
- get rootDir() {
68
- return this.#projectConfig.rootDir ?? "./";
69
- }
70
- get #absoluteProjectDir() {
71
- return this.projectRoot;
72
- }
73
- get include() {
74
- return this.#selectedConfiguration.include ?? this.#projectConfig.include ?? [];
75
- }
76
- get ignore() {
77
- const baseIgnore = this.#projectConfig.ignore ?? [];
78
- const selectedIgnore = this.#selectedConfiguration.ignore ?? [];
79
- return [
80
- ...baseIgnore,
81
- ...selectedIgnore
82
- ];
83
- }
84
- get copyFiles() {
85
- const base = this.#projectConfig.copyFiles ?? [];
86
- const selected = this.#selectedConfiguration.copyFiles ?? [];
87
- return [
88
- ...base,
89
- ...selected
90
- ];
91
- }
92
- get dockerConfig() {
93
- return this.#projectConfig.docker;
94
- }
95
- get uiConfig() {
96
- return this.#projectConfig.ui;
97
- }
98
- get configurations() {
99
- return this.#projectConfig.configurations ?? [];
100
- }
101
- get projectFileChanged$() {
102
- return this.#projectFileChangedSubject.asObservable();
103
- }
104
- async getAllFiles() {
105
- const files = await findFiles(this.include, {
106
- cwd: this.#absoluteProjectDir,
107
- ignore: this.ignore
108
- });
109
- this.#sourceFiles.clear();
110
- for (const file of files){
111
- this.#sourceFiles.set(file.relativePath, file);
112
- }
113
- this.#hasLoadedSourceFiles = true;
114
- return files;
115
- }
116
- isSourceFile(file) {
117
- return this.#sourceFiles.has(file.relativePath);
118
- }
119
- async watch(configuration) {
120
- if (this.#watcher != null) {
121
- return this.#watcher;
122
- }
123
- if (!this.#hasLoadedSourceFiles) {
124
- await this.getAllFiles();
125
- }
126
- const watcher = watchDirectory(this.#absoluteProjectDir, {
127
- ignoreInitial: true
128
- });
129
- this.#watcher = watcher;
130
- watcher.on(WatchEvents.add, (file)=>{
131
- const fileEx = file;
132
- this.updateSourceFileCache(WatchEvents.add, fileEx);
133
- this.#projectFileChangedSubject.next({
134
- project: this,
135
- configuration,
136
- eventName: WatchEvents.add,
137
- file: fileEx
138
- });
139
- });
140
- watcher.on(WatchEvents.change, (file)=>{
141
- const fileEx = file;
142
- this.updateSourceFileCache(WatchEvents.change, fileEx);
143
- this.#projectFileChangedSubject.next({
144
- project: this,
145
- configuration,
146
- eventName: WatchEvents.change,
147
- file: fileEx
148
- });
149
- });
150
- watcher.on(WatchEvents.unlink, (file)=>{
151
- const fileEx = file;
152
- this.updateSourceFileCache(WatchEvents.unlink, fileEx);
153
- this.#projectFileChangedSubject.next({
154
- project: this,
155
- configuration,
156
- eventName: WatchEvents.unlink,
157
- file: fileEx
158
- });
159
- });
160
- return watcher;
161
- }
162
- stopWatching() {
163
- this.#watcher?.close();
164
- this.#watcher = undefined;
165
- }
166
- getRootOut() {
167
- return path.join(this.getWorkspaceOut(), this.#selectedConfiguration.name);
168
- }
169
- getOutDir(target = "") {
170
- const targetDir = this.#selectedConfiguration.pack ? path.join(this.projectDirectory, target) : path.join(this.projectDirectory);
171
- const out = path.join(this.getRootOut(), targetDir);
172
- return out;
173
- }
174
- getConfiguration(name) {
175
- return this.configurations.find((b)=>b.name === name);
176
- }
177
- /**
178
- * The package's entry source file. A TypeScript `main` (the workspace convention,
179
- * `src/index.ts`) is used as written. A `main` or `exports["."]` that describes the published
180
- * layout (`index.js`, `./es6/src/index.js`) is mapped back to the source it was built from,
181
- * falling back to `src/index.ts` (#518).
182
- */ getSourceEntryPath() {
183
- const projectRoot = path.join(this.rootDir, this.projectDirectory);
184
- const main = this.packageJson?.main;
185
- if (main != null && sourceExtensions.isSource(main)) {
186
- return path.join(projectRoot, main);
187
- }
188
- const exports = this.packageJson?.exports;
189
- const root = exports == null ? undefined : typeof exports === "string" ? exports : exports["."] ?? (Object.keys(exports).some((k)=>k.startsWith(".")) ? undefined : exports);
190
- for (const built of [
191
- root == null ? undefined : firstExportTarget(root),
192
- main
193
- ]){
194
- const fromSrc = built?.match(/(?:^|\/)(src\/.+)$/)?.[1];
195
- const source = fromSrc == null ? undefined : sourceFileFor(path.join(projectRoot, fromSrc));
196
- if (source != null) return source;
197
- }
198
- const conventional = path.join(projectRoot, "src/index.ts");
199
- if (main == null || existsSync(conventional)) return conventional;
200
- return path.join(projectRoot, main);
201
- }
202
- /**
203
- * The source file each subpath in package.json `exports` names, such as `./auth` →
204
- * `<project>/src/auth/auth.extensions.ts` (#518). Exports point at the release layout
205
- * (`./es6/src/auth/auth.extensions.js`); the source is the part from `src/`, with its
206
- * TypeScript extension. Wildcard subpaths, and targets with no such source file, are left out.
207
- */ getSourceExports() {
208
- const exports = this.packageJson?.exports;
209
- if (exports == null || typeof exports === "string") return {};
210
- const projectRoot = path.join(this.rootDir, this.projectDirectory);
211
- const sources = {};
212
- for (const [subpath, target] of Object.entries(exports)){
213
- if (!subpath.startsWith("./") || subpath.includes("*")) continue;
214
- const built = firstExportTarget(target);
215
- const fromSrc = built?.match(/(?:^|\/)(src\/.+)$/)?.[1];
216
- if (fromSrc == null) continue;
217
- const source = sourceFileFor(path.join(projectRoot, fromSrc));
218
- if (source != null) sources[subpath] = source;
219
- }
220
- return sources;
221
- }
222
- [Symbol.dispose]() {
223
- this.stopWatching();
224
- }
225
- getWorkspaceOut() {
226
- const out = path.join(this.#projectConfig.rootDir ?? "./", this.#projectConfig.outDir);
227
- return out;
228
- }
229
- updateSourceFileCache(eventName, file) {
230
- if (!this.#hasLoadedSourceFiles) {
231
- return;
232
- }
233
- switch(eventName){
234
- case WatchEvents.unlink:
235
- this.#sourceFiles.delete(file.relativePath);
236
- return;
237
- case WatchEvents.add:
238
- case WatchEvents.change:
239
- if (this.matchesSourcePattern(file.relativePath)) {
240
- this.#sourceFiles.set(file.relativePath, file);
241
- return;
242
- }
243
- this.#sourceFiles.delete(file.relativePath);
244
- return;
245
- }
246
- }
247
- matchesSourcePattern(relativePath) {
248
- if (picomatch.isMatch(relativePath, this.ignore)) {
249
- return false;
250
- }
251
- return picomatch.isMatch(relativePath, this.include);
252
- }
253
- resolveConfiguration(configurationName) {
254
- const resolvedConfigurationName = configurationName ?? "debug";
255
- const configuration = this.configurations.find((c)=>c.name === resolvedConfigurationName);
256
- if (configuration == null) {
257
- throw new WorkspaceConfigurationError(`Configuration "${resolvedConfigurationName}" was not found for project "${this.name}"`);
258
- }
259
- return configuration;
260
- }
261
- }
262
- _ts_decorate([
263
- memoize(),
264
- _ts_metadata("design:type", Function),
265
- _ts_metadata("design:paramtypes", []),
266
- _ts_metadata("design:returntype", Promise)
267
- ], Project.prototype, "getAllFiles", null);
268
- _ts_decorate([
269
- cache(),
270
- _ts_metadata("design:type", Function),
271
- _ts_metadata("design:paramtypes", []),
272
- _ts_metadata("design:returntype", void 0)
273
- ], Project.prototype, "getRootOut", null);
274
- _ts_decorate([
275
- cache(),
276
- _ts_metadata("design:type", Function),
277
- _ts_metadata("design:paramtypes", [
278
- void 0
279
- ]),
280
- _ts_metadata("design:returntype", void 0)
281
- ], Project.prototype, "getOutDir", null);
282
- /** The first path an exports value gives, preferring the conditions that name runtime code. */ function firstExportTarget(value) {
283
- if (typeof value === "string") return value;
284
- for (const condition of [
285
- "import",
286
- "default",
287
- "require",
288
- "node",
289
- "types"
290
- ]){
291
- const nested = value[condition];
292
- if (nested != null) return firstExportTarget(nested);
293
- }
294
- return undefined;
295
- }
296
- /** The TypeScript source a built or declaration path came from, if it exists. */ function sourceFileFor(builtPath) {
297
- // A declaration names the file its JavaScript sits next to: `x.d.ts` is `x.js`.
298
- const declaration = builtPath.match(/^(.*)\.d\.([mc]?)ts$/);
299
- const built = declaration == null ? builtPath : `${declaration[1]}.${declaration[2]}js`;
300
- return sourceExtensions.candidates(built).find((candidate)=>existsSync(candidate));
301
- }
@@ -1,66 +0,0 @@
1
- import path from "node:path";
2
- /** TypeScript source extensions a workspace compiles (#518). */ const SOURCE = [
3
- ".ts",
4
- ".tsx",
5
- ".mts",
6
- ".cts"
7
- ];
8
- /** The sources an import's extension can name: `./a.js` is `a.ts` or `a.tsx`, `./a.mjs` is `a.mts`. */ const SOURCES_FOR = {
9
- ".js": [
10
- ".ts",
11
- ".tsx"
12
- ],
13
- ".jsx": [
14
- ".tsx"
15
- ],
16
- ".mjs": [
17
- ".mts"
18
- ],
19
- ".cjs": [
20
- ".cts"
21
- ],
22
- "": [
23
- ".ts",
24
- ".tsx"
25
- ]
26
- };
27
- /** Whether a path is a TypeScript source (not a declaration file). */ function isSource(file) {
28
- return SOURCE.some((ext)=>file.endsWith(ext)) && !/\.d\.[mc]?ts$/.test(file);
29
- }
30
- /**
31
- * The source files a path written in an import (or a built path) could name, most likely first:
32
- * itself when it is already a source, else the same stem with each matching source extension.
33
- */ function candidates(file) {
34
- if (isSource(file)) return [
35
- file
36
- ];
37
- const ext = path.extname(file);
38
- const known = SOURCES_FOR[ext];
39
- if (known == null) return [
40
- `${file}.ts`,
41
- `${file}.tsx`
42
- ];
43
- const stem = ext === "" ? file : file.slice(0, -ext.length);
44
- return known.map((sourceExt)=>`${stem}${sourceExt}`);
45
- }
46
- /** The extension a source builds to: `.mts` to `.mjs`, `.cts` to `.cjs`, the rest to `.js`. */ function outputExtension(file) {
47
- if (file.endsWith(".mts")) return ".mjs";
48
- if (file.endsWith(".cts")) return ".cjs";
49
- return ".js";
50
- }
51
- /** The path a source builds to: `a.mts` to `a.mjs`, `a.cts` to `a.cjs`, `a.ts` and `a.tsx` to `a.js`. */ function outputPath(file) {
52
- const ext = path.extname(file);
53
- const stem = ext === "" ? file : file.slice(0, -ext.length);
54
- return `${stem}${outputExtension(file)}`;
55
- }
56
- /** Whether a source is CommonJS (`.cts`); everything else compiles as an ES module. */ function isCommonJs(file) {
57
- return file.endsWith(".cts");
58
- }
59
- export const sourceExtensions = {
60
- SOURCE,
61
- isSource,
62
- candidates,
63
- outputExtension,
64
- outputPath,
65
- isCommonJs
66
- };
@@ -1,74 +0,0 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- import { io } from "@decohere/core/io";
4
- import ts from "typescript";
5
- import { WorkspaceConfigurationError } from "../errors.js";
6
- const host = ts.sys;
7
- host.readFile = (file)=>{
8
- return fs.readFileSync(file, {
9
- encoding: "utf8"
10
- });
11
- };
12
- host.readDirectory = (directoryName)=>{
13
- return fs.readdirSync(directoryName, {
14
- withFileTypes: true
15
- }).filter((entry)=>entry.isDirectory()).map((entry)=>entry.name);
16
- };
17
- export const DEFAULT_COMPILER_OPTIONS = {
18
- emitDeclarationOnly: true,
19
- declaration: true,
20
- baseUrl: "./"
21
- };
22
- export class Tsconfig {
23
- static default() {
24
- return new Tsconfig();
25
- }
26
- static async parse(filePath) {
27
- if (!await io.fileExists(filePath)) {
28
- throw new WorkspaceConfigurationError(`TSConfig not found ${filePath}`);
29
- }
30
- const configFileText = await io.readFile(filePath);
31
- const result = ts.parseConfigFileTextToJson(filePath, configFileText);
32
- if (result.error) {
33
- const errorMessage = result.error.messageText.toString();
34
- throw new WorkspaceConfigurationError(`Error parsing ${filePath}. ${errorMessage}`);
35
- }
36
- const dirName = path.resolve(path.dirname(filePath));
37
- const configParseResult = ts.parseJsonConfigFileContent(result.config, host, dirName);
38
- return new Tsconfig({
39
- compilerOptions: configParseResult.options,
40
- errors: configParseResult.errors
41
- });
42
- }
43
- static parseSync(filePath) {
44
- if (!Tsconfig.fileExistsSync(filePath)) {
45
- throw new WorkspaceConfigurationError(`TSConfig not found ${filePath}`);
46
- }
47
- const configFileText = fs.readFileSync(filePath, "utf8");
48
- const result = ts.parseConfigFileTextToJson(filePath, configFileText);
49
- if (result.error) {
50
- const errorMessage = result.error.messageText.toString();
51
- throw new WorkspaceConfigurationError(`Error parsing ${filePath}. ${errorMessage}`);
52
- }
53
- const dirName = path.resolve(path.dirname(filePath));
54
- const configParseResult = ts.parseJsonConfigFileContent(result.config, host, dirName);
55
- return new Tsconfig({
56
- compilerOptions: configParseResult.options,
57
- errors: configParseResult.errors
58
- });
59
- }
60
- static fileExistsSync(filePath) {
61
- try {
62
- return fs.statSync(filePath).isFile();
63
- } catch {
64
- return false;
65
- }
66
- }
67
- constructor(data = {
68
- compilerOptions: DEFAULT_COMPILER_OPTIONS
69
- }){
70
- Object.assign(this, data);
71
- }
72
- compilerOptions;
73
- errors;
74
- }
@@ -1,4 +0,0 @@
1
- /**
2
- * Generic configuration for a workspace build profile.
3
- * Concrete tools (e.g. @decohere/forge) extend this with tool-specific fields.
4
- */ export { };
@@ -1,143 +0,0 @@
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 { injectable } from "@decohere/crucible/decorators";
11
- import { collections } from "@decohere/core/collections";
12
- import { mixwith } from "@decohere/core/extensibility";
13
- import { Subject } from "rxjs";
14
- import { WorkspaceConfigurationError } from "../errors.js";
15
- import { HasPackageJson } from "./has-package-json.mixin.js";
16
- import { HasTsConfig } from "./has-tsconfig.mixin.js";
17
- import { packageAliases } from "./package-aliases.js";
18
- import { PackageJson } from "./package-json.model.js";
19
- import { Project } from "./project.model.js";
20
- export class Workspace extends mixwith(HasPackageJson, HasTsConfig) {
21
- packageJson;
22
- #name;
23
- #config;
24
- #projectSeeds = [];
25
- #projectCache = new Map();
26
- /**
27
- * Each project's source file paths as a set, so `findProject` is a lookup rather than a scan
28
- * of every file: the Vitest plugin asks once per transformed file (#517). Keyed by the list
29
- * `getAllFiles` returns, so a new list (after its cache is flushed) gets a new set.
30
- */ #sourcePaths = new WeakMap();
31
- #projectFileChangedSubject = new Subject();
32
- #projectWatchSubscriptions = new Map();
33
- #watchedProjects = [];
34
- constructor(options = {}){
35
- super();
36
- const { packageJson, config, projects } = options;
37
- this.#name = packageJson?.name ?? "WORKSPACE";
38
- this.packageJson = packageJson ?? new PackageJson();
39
- this.#config = config ?? {};
40
- this.#projectSeeds = projects ?? [];
41
- this.validateWorkspace();
42
- }
43
- get name() {
44
- return this.#name;
45
- }
46
- get projectSeeds() {
47
- return this.#projectSeeds;
48
- }
49
- getProjects(configuration) {
50
- const cached = this.#projectCache.get(configuration);
51
- if (cached != null) {
52
- return cached;
53
- }
54
- const projects = this.#projectSeeds.map((seed)=>new Project(seed.name, seed.config ?? {}, configuration));
55
- this.#projectCache.set(configuration, projects);
56
- return projects;
57
- }
58
- get rootDir() {
59
- return this.#config.rootDir ?? "./";
60
- }
61
- get packageJsonDirectory() {
62
- return this.rootDir;
63
- }
64
- get tsConfigDirectory() {
65
- return this.rootDir;
66
- }
67
- get fallbackTsConfigDirectory() {
68
- return undefined;
69
- }
70
- /**
71
- * Package-name aliases for this configuration: workspace convention, then
72
- * workspace `tsconfig.json` `paths` overrides. Forge and Flux must use this
73
- * instead of assembling the map themselves.
74
- */ getPackageAliases(configuration) {
75
- const tsconfig = this.findTsConfigPath() != null ? this.getTsConfigSync() : undefined;
76
- const baseUrl = typeof tsconfig?.compilerOptions?.baseUrl === "string" && tsconfig.compilerOptions.baseUrl.length > 0 ? tsconfig.compilerOptions.baseUrl : this.rootDir;
77
- return packageAliases.build(this.getProjects(configuration), {
78
- baseUrl,
79
- tsconfigPaths: tsconfig?.compilerOptions?.paths
80
- });
81
- }
82
- async watch(configuration) {
83
- this.stopWatching();
84
- const projects = this.getProjects(configuration);
85
- this.#watchedProjects = projects;
86
- for (const project of projects){
87
- await project.watch(configuration);
88
- const subscription = project.projectFileChanged$.subscribe((evt)=>this.#projectFileChangedSubject.next(evt));
89
- this.#projectWatchSubscriptions.set(project.projectDirectory, subscription);
90
- }
91
- }
92
- stopWatching() {
93
- for (const subscription of this.#projectWatchSubscriptions.values()){
94
- subscription.unsubscribe();
95
- }
96
- for (const project of this.#watchedProjects){
97
- project.stopWatching();
98
- }
99
- this.#watchedProjects = [];
100
- this.#projectWatchSubscriptions.clear();
101
- }
102
- [Symbol.dispose]() {
103
- this.stopWatching();
104
- }
105
- get projectFileChanged$() {
106
- return this.#projectFileChangedSubject.asObservable();
107
- }
108
- async findProject(filePath, buildConfig) {
109
- for (const project of this.getProjects(buildConfig)){
110
- if ((await this.sourcePathsOf(project)).has(filePath)) {
111
- return project;
112
- }
113
- }
114
- return undefined;
115
- }
116
- async sourcePathsOf(project) {
117
- const files = await project.getAllFiles();
118
- const cached = this.#sourcePaths.get(files);
119
- if (cached != null) return cached;
120
- const paths = new Set(files.map((file)=>file.fullPath));
121
- this.#sourcePaths.set(files, paths);
122
- return paths;
123
- }
124
- validateWorkspace() {
125
- const errors = this.findDuplicateProjectSeeds(this.#projectSeeds);
126
- if (errors.length > 0) {
127
- throw new WorkspaceConfigurationError(errors.join("\n"));
128
- }
129
- }
130
- findDuplicateProjectSeeds(seeds) {
131
- const duplicates = collections.findDuplicatesByKey([
132
- ...seeds
133
- ], "name");
134
- return duplicates.map(([name, group])=>`Duplicate project name "${name}" found at: ${group.map((seed)=>seed.config?.projectDir ?? name).join(", ")}`);
135
- }
136
- }
137
- Workspace = _ts_decorate([
138
- injectable(),
139
- _ts_metadata("design:type", Function),
140
- _ts_metadata("design:paramtypes", [
141
- typeof Partial === "undefined" ? Object : Partial
142
- ])
143
- ], Workspace);
@@ -1,8 +0,0 @@
1
- export declare const TS_CONFIG = "tsconfig.json";
2
- export declare const PACKAGE_JSON = "package.json";
3
- export declare const NODE_MODULES = "node_modules";
4
- export declare const FIXTURES = "__fixtures__";
5
- export declare const WORKSPACE_CONFIG = ".decohere.js";
6
- export declare const PROJECTS_DIR = "projects";
7
- import type { WorkspaceOptions } from "./models/workspace-config.type.js";
8
- export declare const WorkspaceDefaults: WorkspaceOptions;
@@ -1,2 +0,0 @@
1
- export declare class WorkspaceConfigurationError extends Error {
2
- }
@@ -1,6 +0,0 @@
1
- import type { WorkspaceOptions } from "../models/workspace-config.type.js";
2
- declare class ConfigFactory {
3
- getUserConfig(configFile?: string): Promise<Partial<WorkspaceOptions>>;
4
- }
5
- export declare const UserConfigFactory: ConfigFactory;
6
- export {};
@@ -1,6 +0,0 @@
1
- /**
2
- * The workspace root for a command run from `startDir`: the nearest directory at or above
3
- * it with a `package.json` and a `projects/` folder. Falls back to `startDir`, so running
4
- * from a package directory (`projects/api`) still sees the whole workspace.
5
- */
6
- export declare function findWorkspaceRoot(startDir: string): string;
@@ -1,8 +0,0 @@
1
- import { Project, type ProjectSeed } from "../models/project.model.js";
2
- import type { WorkspaceOptions } from "../models/workspace-config.type.js";
3
- export declare class ProjectFactory {
4
- static getProjectSeeds(config: WorkspaceOptions): Promise<ProjectSeed[]>;
5
- private static filterNestedPackageJsonFiles;
6
- static getProjects(config: WorkspaceOptions, configuration?: string): Promise<Project[]>;
7
- private static projectFrom;
8
- }
@@ -1,9 +0,0 @@
1
- import { Workspace } from "../models/workspace.model.js";
2
- import type { WorkspaceFactoryOptions, WorkspaceOptions } from "../models/workspace-config.type.js";
3
- declare class Factory {
4
- create(options?: WorkspaceFactoryOptions & Partial<WorkspaceOptions>): Promise<Workspace>;
5
- private getMergedConfig;
6
- private getPackageJson;
7
- }
8
- export declare const WorkspaceFactory: Factory;
9
- export {};
@@ -1,9 +0,0 @@
1
- import { PackageJson } from "./package-json.model.js";
2
- export declare abstract class HasPackageJson {
3
- protected abstract packageJson?: PackageJson;
4
- protected abstract get packageJsonDirectory(): string;
5
- getPackageJson(): Promise<PackageJson>;
6
- get packageJsonPath(): string;
7
- getVersion(): Promise<string>;
8
- persistPackageJson(packageJson: PackageJson): Promise<void>;
9
- }
@@ -1,11 +0,0 @@
1
- import { Tsconfig } from "./tsconfig.model.js";
2
- export declare abstract class HasTsConfig {
3
- protected abstract get tsConfigDirectory(): string;
4
- protected abstract get fallbackTsConfigDirectory(): string | undefined;
5
- getTsConfig(): Promise<Tsconfig>;
6
- getTsConfigSync(): Tsconfig;
7
- /** First existing `tsconfig.json` on this object, if any. */
8
- protected findTsConfigPath(): string | undefined;
9
- private get tsConfigPaths();
10
- private static fileExistsSync;
11
- }