@decohere/workspace 0.0.1-dev.116
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 +81 -0
- package/commonjs/src/const.js +69 -0
- package/commonjs/src/errors.js +12 -0
- package/commonjs/src/factories/config.factory.js +90 -0
- package/commonjs/src/factories/find-workspace-root.js +37 -0
- package/commonjs/src/factories/project.factory.js +72 -0
- package/commonjs/src/factories/workspace.factory.js +50 -0
- package/commonjs/src/index.js +74 -0
- package/commonjs/src/models/has-package-json.mixin.js +50 -0
- package/commonjs/src/models/has-tsconfig.mixin.js +75 -0
- package/commonjs/src/models/package-aliases.js +112 -0
- package/commonjs/src/models/package-json.model.js +196 -0
- package/commonjs/src/models/project-file-event.js +4 -0
- package/commonjs/src/models/project.model.js +316 -0
- package/commonjs/src/models/source-extensions.js +81 -0
- package/commonjs/src/models/tsconfig.model.js +97 -0
- package/commonjs/src/models/workspace-config.type.js +7 -0
- package/commonjs/src/models/workspace.model.js +153 -0
- package/es6/src/const.js +36 -0
- package/es6/src/errors.js +2 -0
- package/es6/src/factories/config.factory.js +34 -0
- package/es6/src/factories/find-workspace-root.js +26 -0
- package/es6/src/factories/project.factory.js +57 -0
- package/es6/src/factories/workspace.factory.js +35 -0
- package/es6/src/index.js +14 -0
- package/es6/src/models/has-package-json.mixin.js +35 -0
- package/es6/src/models/has-tsconfig.mixin.js +60 -0
- package/es6/src/models/package-aliases.js +97 -0
- package/es6/src/models/package-json.model.js +186 -0
- package/es6/src/models/project-file-event.js +1 -0
- package/es6/src/models/project.model.js +301 -0
- package/es6/src/models/source-extensions.js +66 -0
- package/es6/src/models/tsconfig.model.js +74 -0
- package/es6/src/models/workspace-config.type.js +4 -0
- package/es6/src/models/workspace.model.js +143 -0
- package/package.json +34 -0
- package/types/src/const.d.ts +8 -0
- package/types/src/errors.d.ts +2 -0
- package/types/src/factories/config.factory.d.ts +6 -0
- package/types/src/factories/find-workspace-root.d.ts +6 -0
- package/types/src/factories/project.factory.d.ts +8 -0
- package/types/src/factories/workspace.factory.d.ts +9 -0
- package/types/src/index.d.ts +18 -0
- package/types/src/models/has-package-json.mixin.d.ts +9 -0
- package/types/src/models/has-tsconfig.mixin.d.ts +11 -0
- package/types/src/models/package-aliases.d.ts +28 -0
- package/types/src/models/package-json.model.d.ts +39 -0
- package/types/src/models/project-file-event.d.ts +8 -0
- package/types/src/models/project.model.d.ts +64 -0
- package/types/src/models/source-extensions.d.ts +22 -0
- package/types/src/models/tsconfig.model.d.ts +11 -0
- package/types/src/models/workspace-config.type.d.ts +52 -0
- package/types/src/models/workspace.model.d.ts +40 -0
|
@@ -0,0 +1,74 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
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);
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@decohere/workspace",
|
|
3
|
+
"version": "0.0.1-dev.116",
|
|
4
|
+
"description": "Workspace and project model primitives for monorepo tooling",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"typescript",
|
|
7
|
+
"monorepo",
|
|
8
|
+
"workspace",
|
|
9
|
+
"project-model",
|
|
10
|
+
"tsconfig",
|
|
11
|
+
"decohere"
|
|
12
|
+
],
|
|
13
|
+
"license": "ISC",
|
|
14
|
+
"author": "Typesmiths",
|
|
15
|
+
"type": "module",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./types/src/index.d.ts",
|
|
19
|
+
"import": "./es6/src/index.js",
|
|
20
|
+
"require": "./commonjs/src/index.js"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@decohere/core": "0.0.1-dev.116",
|
|
28
|
+
"@decohere/crucible": "0.0.1-dev.116",
|
|
29
|
+
"picomatch": "^4.0.4",
|
|
30
|
+
"rxjs": "^7.8.2",
|
|
31
|
+
"typescript": "^6.0.3"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {}
|
|
34
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
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;
|
|
@@ -0,0 +1,6 @@
|
|
|
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;
|
|
@@ -0,0 +1,8 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
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 {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export { WORKSPACE_CONFIG, WorkspaceDefaults } from "./const.js";
|
|
2
|
+
export { WorkspaceConfigurationError } from "./errors.js";
|
|
3
|
+
export { UserConfigFactory } from "./factories/config.factory.js";
|
|
4
|
+
export { findWorkspaceRoot } from "./factories/find-workspace-root.js";
|
|
5
|
+
export { ProjectFactory } from "./factories/project.factory.js";
|
|
6
|
+
export { WorkspaceFactory } from "./factories/workspace.factory.js";
|
|
7
|
+
export { HasPackageJson } from "./models/has-package-json.mixin.js";
|
|
8
|
+
export { HasTsConfig } from "./models/has-tsconfig.mixin.js";
|
|
9
|
+
export { type PackageAliases, packageAliases } from "./models/package-aliases.js";
|
|
10
|
+
export type { PackageExports, RepositoryReference } from "./models/package-json.model.js";
|
|
11
|
+
export { PackageJson } from "./models/package-json.model.js";
|
|
12
|
+
export type { ProjectConfig, ProjectSeed } from "./models/project.model.js";
|
|
13
|
+
export { Project } from "./models/project.model.js";
|
|
14
|
+
export { sourceExtensions } from "./models/source-extensions.js";
|
|
15
|
+
export type { ProjectFileEvent } from "./models/project-file-event.js";
|
|
16
|
+
export { DEFAULT_COMPILER_OPTIONS, Tsconfig } from "./models/tsconfig.model.js";
|
|
17
|
+
export { Workspace } from "./models/workspace.model.js";
|
|
18
|
+
export type { Configuration, ProjectOptions, UiConfig, WorkspaceFactoryOptions, WorkspaceOptions, } from "./models/workspace-config.type.js";
|
|
@@ -0,0 +1,9 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Project } from "./project.model.js";
|
|
2
|
+
export type PackageAliases = {
|
|
3
|
+
/** Absolute base for SWC `jsc.paths` / specifier resolution. */
|
|
4
|
+
baseUrl: string;
|
|
5
|
+
/** Package-name and `name/*` keys, values relative to `baseUrl`. */
|
|
6
|
+
paths: Record<string, string[]>;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Convention aliases from workspace packages, then tsconfig `paths` for the same keys.
|
|
10
|
+
* Prefer `Workspace.getPackageAliases` when a workspace is available.
|
|
11
|
+
*/
|
|
12
|
+
declare function build(projects: readonly Project[], options: {
|
|
13
|
+
baseUrl: string;
|
|
14
|
+
tsconfigPaths?: Record<string, string[]>;
|
|
15
|
+
}): PackageAliases;
|
|
16
|
+
/** Bare import specifier that is not relative, absolute, or a node/file URL. */
|
|
17
|
+
declare function isBareSpecifier(specifier: string): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Resolve a bare specifier through package aliases (SWC-style `paths`
|
|
20
|
+
* relative to `baseUrl`) to an absolute `.ts` path. Longest key wins.
|
|
21
|
+
*/
|
|
22
|
+
declare function resolvePath(specifier: string, paths: Record<string, string[]>, baseUrl: string): string | null;
|
|
23
|
+
export declare const packageAliases: {
|
|
24
|
+
build: typeof build;
|
|
25
|
+
isBareSpecifier: typeof isBareSpecifier;
|
|
26
|
+
resolvePath: typeof resolvePath;
|
|
27
|
+
};
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export type RepositoryReference = string | {
|
|
2
|
+
type?: string;
|
|
3
|
+
url?: string;
|
|
4
|
+
directory?: string;
|
|
5
|
+
};
|
|
6
|
+
export type PackageExports = string | {
|
|
7
|
+
[key: string]: PackageExports;
|
|
8
|
+
};
|
|
9
|
+
export declare class PackageJson {
|
|
10
|
+
private static readonly PACKAGE_JSON_KEY_ORDER;
|
|
11
|
+
constructor(data?: Partial<PackageJson>);
|
|
12
|
+
sort(): this;
|
|
13
|
+
withVersion(version: string): PackageJson;
|
|
14
|
+
withDependencies(dependencies: Record<string, string>): PackageJson;
|
|
15
|
+
withDevDependencies(devDependencies: Record<string, string>): PackageJson;
|
|
16
|
+
toJSON(): string;
|
|
17
|
+
getRepositoryOwner(): string;
|
|
18
|
+
getRepositoryName(): string;
|
|
19
|
+
getRepositoryFullName(): string;
|
|
20
|
+
private sortDependencyMap;
|
|
21
|
+
private sortPackageJsonKeys;
|
|
22
|
+
private toSortedPackageJsonRecord;
|
|
23
|
+
private repositoryReferenceToString;
|
|
24
|
+
private parseRepository;
|
|
25
|
+
private parseRepositoryFromUrl;
|
|
26
|
+
private sanitizeRepositoryName;
|
|
27
|
+
readonly name: string;
|
|
28
|
+
version: string;
|
|
29
|
+
readonly repository?: RepositoryReference;
|
|
30
|
+
readonly main: string;
|
|
31
|
+
/** `exports` as written in package.json: a path, or a map of subpaths (values may be condition maps). */
|
|
32
|
+
readonly exports?: PackageExports;
|
|
33
|
+
readonly peerDependencies?: Record<string, string>;
|
|
34
|
+
readonly optionalDependencies?: Record<string, string>;
|
|
35
|
+
readonly scripts: Record<string, string>;
|
|
36
|
+
dependencies: Record<string, string>;
|
|
37
|
+
devDependencies: Record<string, string>;
|
|
38
|
+
readonly private?: boolean;
|
|
39
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { type FileEx, type Watcher } from "@decohere/core/io";
|
|
2
|
+
import { HasPackageJson } from "./has-package-json.mixin.js";
|
|
3
|
+
import { HasTsConfig } from "./has-tsconfig.mixin.js";
|
|
4
|
+
import { PackageJson } from "./package-json.model.js";
|
|
5
|
+
import type { ProjectFileEvent } from "./project-file-event.js";
|
|
6
|
+
import type { Configuration, WorkspaceOptions } from "./workspace-config.type.js";
|
|
7
|
+
export type ProjectConfig = {
|
|
8
|
+
projectDir?: string;
|
|
9
|
+
packageJson?: PackageJson;
|
|
10
|
+
} & WorkspaceOptions;
|
|
11
|
+
export type ProjectSeed = {
|
|
12
|
+
name: string;
|
|
13
|
+
config?: ProjectConfig;
|
|
14
|
+
};
|
|
15
|
+
declare const Project_base: abstract new (...args: unknown[]) => HasPackageJson & HasTsConfig;
|
|
16
|
+
export declare class Project extends Project_base {
|
|
17
|
+
#private;
|
|
18
|
+
protected packageJson?: PackageJson;
|
|
19
|
+
constructor(name: string, config?: ProjectConfig, selectedConfigurationName?: string);
|
|
20
|
+
get name(): string;
|
|
21
|
+
get isPrivate(): boolean;
|
|
22
|
+
toString(): string;
|
|
23
|
+
get projectDirectory(): string;
|
|
24
|
+
get projectRoot(): string;
|
|
25
|
+
get configuration(): Configuration;
|
|
26
|
+
protected get packageJsonDirectory(): string;
|
|
27
|
+
protected get tsConfigDirectory(): string;
|
|
28
|
+
protected get fallbackTsConfigDirectory(): string | undefined;
|
|
29
|
+
get rootDir(): string;
|
|
30
|
+
get include(): string[];
|
|
31
|
+
get ignore(): string[];
|
|
32
|
+
get copyFiles(): string[];
|
|
33
|
+
get dockerConfig(): import("./workspace-config.type.js").DockerConfig;
|
|
34
|
+
get uiConfig(): import("./workspace-config.type.js").UiConfig;
|
|
35
|
+
get configurations(): Configuration[];
|
|
36
|
+
get projectFileChanged$(): import("rxjs").Observable<ProjectFileEvent>;
|
|
37
|
+
getAllFiles(): Promise<FileEx[]>;
|
|
38
|
+
isSourceFile(file: FileEx): boolean;
|
|
39
|
+
watch(configuration: string): Promise<Watcher>;
|
|
40
|
+
stopWatching(): void;
|
|
41
|
+
getRootOut(): string;
|
|
42
|
+
getOutDir(target?: string): string;
|
|
43
|
+
getConfiguration(name: string): Configuration;
|
|
44
|
+
/**
|
|
45
|
+
* The package's entry source file. A TypeScript `main` (the workspace convention,
|
|
46
|
+
* `src/index.ts`) is used as written. A `main` or `exports["."]` that describes the published
|
|
47
|
+
* layout (`index.js`, `./es6/src/index.js`) is mapped back to the source it was built from,
|
|
48
|
+
* falling back to `src/index.ts` (#518).
|
|
49
|
+
*/
|
|
50
|
+
getSourceEntryPath(): string;
|
|
51
|
+
/**
|
|
52
|
+
* The source file each subpath in package.json `exports` names, such as `./auth` →
|
|
53
|
+
* `<project>/src/auth/auth.extensions.ts` (#518). Exports point at the release layout
|
|
54
|
+
* (`./es6/src/auth/auth.extensions.js`); the source is the part from `src/`, with its
|
|
55
|
+
* TypeScript extension. Wildcard subpaths, and targets with no such source file, are left out.
|
|
56
|
+
*/
|
|
57
|
+
getSourceExports(): Record<string, string>;
|
|
58
|
+
[Symbol.dispose](): void;
|
|
59
|
+
private getWorkspaceOut;
|
|
60
|
+
private updateSourceFileCache;
|
|
61
|
+
private matchesSourcePattern;
|
|
62
|
+
private resolveConfiguration;
|
|
63
|
+
}
|
|
64
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Whether a path is a TypeScript source (not a declaration file). */
|
|
2
|
+
declare function isSource(file: string): boolean;
|
|
3
|
+
/**
|
|
4
|
+
* The source files a path written in an import (or a built path) could name, most likely first:
|
|
5
|
+
* itself when it is already a source, else the same stem with each matching source extension.
|
|
6
|
+
*/
|
|
7
|
+
declare function candidates(file: string): string[];
|
|
8
|
+
/** The extension a source builds to: `.mts` to `.mjs`, `.cts` to `.cjs`, the rest to `.js`. */
|
|
9
|
+
declare function outputExtension(file: string): string;
|
|
10
|
+
/** The path a source builds to: `a.mts` to `a.mjs`, `a.cts` to `a.cjs`, `a.ts` and `a.tsx` to `a.js`. */
|
|
11
|
+
declare function outputPath(file: string): string;
|
|
12
|
+
/** Whether a source is CommonJS (`.cts`); everything else compiles as an ES module. */
|
|
13
|
+
declare function isCommonJs(file: string): boolean;
|
|
14
|
+
export declare const sourceExtensions: {
|
|
15
|
+
SOURCE: readonly [".ts", ".tsx", ".mts", ".cts"];
|
|
16
|
+
isSource: typeof isSource;
|
|
17
|
+
candidates: typeof candidates;
|
|
18
|
+
outputExtension: typeof outputExtension;
|
|
19
|
+
outputPath: typeof outputPath;
|
|
20
|
+
isCommonJs: typeof isCommonJs;
|
|
21
|
+
};
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import ts, { type CompilerOptions } from "typescript";
|
|
2
|
+
export declare const DEFAULT_COMPILER_OPTIONS: CompilerOptions;
|
|
3
|
+
export declare class Tsconfig {
|
|
4
|
+
static default(): Tsconfig;
|
|
5
|
+
static parse(filePath: string): Promise<Tsconfig>;
|
|
6
|
+
static parseSync(filePath: string): Tsconfig;
|
|
7
|
+
private static fileExistsSync;
|
|
8
|
+
constructor(data?: Partial<Tsconfig>);
|
|
9
|
+
readonly compilerOptions: CompilerOptions;
|
|
10
|
+
readonly errors: readonly ts.Diagnostic[];
|
|
11
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic configuration for a workspace build profile.
|
|
3
|
+
* Concrete tools (e.g. @decohere/forge) extend this with tool-specific fields.
|
|
4
|
+
*/
|
|
5
|
+
export type Configuration = {
|
|
6
|
+
name: string;
|
|
7
|
+
include?: string[];
|
|
8
|
+
ignore?: string[];
|
|
9
|
+
/** Extra globs to copy into the configuration outDir (non-transpiled assets). */
|
|
10
|
+
copyFiles?: string[];
|
|
11
|
+
/** If true, the output is packed into a folder per project directory. */
|
|
12
|
+
pack?: boolean;
|
|
13
|
+
};
|
|
14
|
+
export type DockerConfig = {
|
|
15
|
+
image?: string;
|
|
16
|
+
dockerfile?: string;
|
|
17
|
+
buildArgs?: Record<string, string>;
|
|
18
|
+
secrets?: Record<string, string>;
|
|
19
|
+
tags?: string[];
|
|
20
|
+
push?: boolean;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* The UI a deployable serves from its API (`withStatic` in `@decohere/http`). `forge package` copies the
|
|
24
|
+
* UI project's build output into the deployable's `.package/` folder.
|
|
25
|
+
*/
|
|
26
|
+
export type UiConfig = {
|
|
27
|
+
/** Package name of the UI project, e.g. `@app/shop.web`. */
|
|
28
|
+
project: string;
|
|
29
|
+
/** The UI project's build output folder, relative to that project. Defaults to `dist`. */
|
|
30
|
+
output?: string;
|
|
31
|
+
/** Where it lands inside `.package/`, which is also the folder the API serves. Defaults to `ui`. */
|
|
32
|
+
to?: string;
|
|
33
|
+
};
|
|
34
|
+
export type WorkspaceFactoryOptions = {
|
|
35
|
+
rootDir?: string;
|
|
36
|
+
configFile?: string;
|
|
37
|
+
};
|
|
38
|
+
export type ProjectOptions = {
|
|
39
|
+
include?: string[];
|
|
40
|
+
ignore?: string[];
|
|
41
|
+
copyFiles: string[];
|
|
42
|
+
configurations: Configuration[];
|
|
43
|
+
docker?: DockerConfig;
|
|
44
|
+
ui?: UiConfig;
|
|
45
|
+
};
|
|
46
|
+
export type WorkspaceOptions = {
|
|
47
|
+
rootDir?: string;
|
|
48
|
+
outDir: string;
|
|
49
|
+
includeProjects: string[];
|
|
50
|
+
ignoreProjects: string[];
|
|
51
|
+
projects?: Record<string, Partial<ProjectOptions>>;
|
|
52
|
+
} & ProjectOptions;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { HasPackageJson } from "./has-package-json.mixin.js";
|
|
2
|
+
import { HasTsConfig } from "./has-tsconfig.mixin.js";
|
|
3
|
+
import { type PackageAliases } from "./package-aliases.js";
|
|
4
|
+
import { PackageJson } from "./package-json.model.js";
|
|
5
|
+
import { Project, type ProjectSeed } from "./project.model.js";
|
|
6
|
+
import type { ProjectFileEvent } from "./project-file-event.js";
|
|
7
|
+
import type { WorkspaceOptions } from "./workspace-config.type.js";
|
|
8
|
+
export type WorkspaceInitOptions = {
|
|
9
|
+
packageJson?: PackageJson;
|
|
10
|
+
config: Partial<WorkspaceOptions>;
|
|
11
|
+
projects: ProjectSeed[];
|
|
12
|
+
};
|
|
13
|
+
declare const Workspace_base: abstract new (...args: unknown[]) => HasPackageJson & HasTsConfig;
|
|
14
|
+
export declare class Workspace extends Workspace_base {
|
|
15
|
+
#private;
|
|
16
|
+
protected packageJson?: PackageJson;
|
|
17
|
+
constructor(options?: Partial<WorkspaceInitOptions>);
|
|
18
|
+
get name(): string;
|
|
19
|
+
protected get projectSeeds(): readonly ProjectSeed[];
|
|
20
|
+
getProjects(configuration: string): readonly Project[];
|
|
21
|
+
get rootDir(): string;
|
|
22
|
+
get packageJsonDirectory(): string;
|
|
23
|
+
protected get tsConfigDirectory(): string;
|
|
24
|
+
protected get fallbackTsConfigDirectory(): string | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* Package-name aliases for this configuration: workspace convention, then
|
|
27
|
+
* workspace `tsconfig.json` `paths` overrides. Forge and Flux must use this
|
|
28
|
+
* instead of assembling the map themselves.
|
|
29
|
+
*/
|
|
30
|
+
getPackageAliases(configuration: string): PackageAliases;
|
|
31
|
+
watch(configuration: string): Promise<void>;
|
|
32
|
+
stopWatching(): void;
|
|
33
|
+
[Symbol.dispose](): void;
|
|
34
|
+
get projectFileChanged$(): import("rxjs").Observable<ProjectFileEvent>;
|
|
35
|
+
findProject(filePath: string, buildConfig: string): Promise<Project | undefined>;
|
|
36
|
+
private sourcePathsOf;
|
|
37
|
+
private validateWorkspace;
|
|
38
|
+
private findDuplicateProjectSeeds;
|
|
39
|
+
}
|
|
40
|
+
export {};
|