@decohere/workspace 0.0.1-dev.129 → 0.0.1-dev.130
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 +4 -2
- package/commonjs/src/factories/project.factory.js +31 -0
- package/commonjs/src/index.js +0 -4
- package/commonjs/src/models/workspace.model.js +7 -0
- package/es6/src/factories/project.factory.js +31 -0
- package/es6/src/index.js +0 -1
- package/es6/src/models/workspace.model.js +7 -0
- package/package.json +4 -4
- package/types/src/factories/project.factory.d.ts +7 -0
- package/types/src/index.d.ts +0 -1
- package/types/src/models/workspace.model.d.ts +6 -0
- package/commonjs/src/factories/find-workspace-root.js +0 -37
- package/es6/src/factories/find-workspace-root.js +0 -26
- package/types/src/factories/find-workspace-root.d.ts +0 -6
package/README.md
CHANGED
|
@@ -4,10 +4,12 @@ Workspace and project model for Decohere tooling. Forge and Flux call it to find
|
|
|
4
4
|
|
|
5
5
|
## Workspace layout
|
|
6
6
|
|
|
7
|
-
A **project** is a directory whose root contains a `package.json`. The workspace package discovers those files with `includeProjects` globs
|
|
7
|
+
A **project** is a directory whose root contains a `package.json`. The workspace package discovers those files with `includeProjects` globs from the workspace root: the folder the tool runs in. Nothing searches the folders above it; to run from elsewhere, pass `--root-dir <dir>` to `forge` or `flux`.
|
|
8
8
|
|
|
9
9
|
The workspace root may also have a `package.json`. That file is workspace metadata (name/version). It is **not** a project unless it also matches `includeProjects`.
|
|
10
10
|
|
|
11
|
+
**Standalone projects.** A folder with a `package.json` and no `projects/` folder, and no `includeProjects` of your own, is one project: its root `package.json` is the package, with its sources at the root (build output, `node_modules` and `.git` excluded). Every tool works on that one package; `forge dependencies sync`, `dependencies report` and `paths` have nothing to do, since there is no other package. Adding a `projects/` folder turns it into a monorepo: move the sources into `projects/<name>/` with their own `package.json`.
|
|
12
|
+
|
|
11
13
|
### Defaults
|
|
12
14
|
|
|
13
15
|
| Option | Default |
|
|
@@ -42,7 +44,7 @@ export default {
|
|
|
42
44
|
|
|
43
45
|
Per-project settings go under `projects`, keyed by package name: `docker` (image, Dockerfile, tags, build args) and `ui` (the UI project a deployable ships, see [forge](../forge/README.md#shipping-the-ui-with-the-api)).
|
|
44
46
|
|
|
45
|
-
`--config-file` on `forge` / `flux` selects a different config path. The default
|
|
47
|
+
`--config-file` on `forge` / `flux` selects a different config path. The default is `.decohere.js` in the workspace root (`--root-dir`, else the current folder).
|
|
46
48
|
|
|
47
49
|
### Package aliases
|
|
48
50
|
|
|
@@ -12,6 +12,7 @@ const _nodepath = /*#__PURE__*/ _interop_require_default(require("node:path"));
|
|
|
12
12
|
const _io = require("@decohere/core/io");
|
|
13
13
|
const _io1 = require("@decohere/devkit/io");
|
|
14
14
|
const _objects = require("@decohere/core/objects");
|
|
15
|
+
const _const = require("../const.js");
|
|
15
16
|
const _errors = require("../errors.js");
|
|
16
17
|
const _packagejsonmodel = require("../models/package-json.model.js");
|
|
17
18
|
const _projectmodel = require("../models/project.model.js");
|
|
@@ -29,9 +30,39 @@ class ProjectFactory {
|
|
|
29
30
|
ignore: ignoreProjects
|
|
30
31
|
});
|
|
31
32
|
const packageJsonFiles = ProjectFactory.filterNestedPackageJsonFiles(discoveredPackageJsonFiles);
|
|
33
|
+
if (packageJsonFiles.length === 0 && await ProjectFactory.isStandalone(config)) {
|
|
34
|
+
return [
|
|
35
|
+
await ProjectFactory.standaloneProject(config)
|
|
36
|
+
];
|
|
37
|
+
}
|
|
32
38
|
const projects = await Promise.all(packageJsonFiles.map((file)=>ProjectFactory.projectFrom(file, config)));
|
|
33
39
|
return projects.sort((a, b)=>a.name.localeCompare(b.name));
|
|
34
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* A standalone project: no `projects/` folder and no `includeProjects` of the user's own, but
|
|
43
|
+
* a root `package.json`. That `package.json` is then the one project, at the root.
|
|
44
|
+
*/ static async isStandalone(config) {
|
|
45
|
+
const rootDir = config.rootDir ?? "./";
|
|
46
|
+
const defaultInclude = JSON.stringify(config.includeProjects) === JSON.stringify(_const.WorkspaceDefaults.includeProjects);
|
|
47
|
+
return defaultInclude && !await _io.io.directoryExists(_nodepath.default.join(rootDir, _const.PROJECTS_DIR)) && await _io.io.fileExists(_nodepath.default.join(rootDir, _const.PACKAGE_JSON));
|
|
48
|
+
}
|
|
49
|
+
/** The root `package.json` as the one project. Build output and `.git` aren't its sources. */ static async standaloneProject(config) {
|
|
50
|
+
const [packageJsonFile] = await (0, _io1.findFiles)([
|
|
51
|
+
_const.PACKAGE_JSON
|
|
52
|
+
], {
|
|
53
|
+
cwd: config.rootDir ?? "./"
|
|
54
|
+
});
|
|
55
|
+
const outDir = (config.outDir ?? _const.WorkspaceDefaults.outDir).replace(/[/\\]+$/, "");
|
|
56
|
+
const ignore = [
|
|
57
|
+
...config.ignore ?? [],
|
|
58
|
+
`${outDir}/**`,
|
|
59
|
+
".git/**"
|
|
60
|
+
];
|
|
61
|
+
return ProjectFactory.projectFrom(packageJsonFile, {
|
|
62
|
+
...config,
|
|
63
|
+
ignore
|
|
64
|
+
});
|
|
65
|
+
}
|
|
35
66
|
static filterNestedPackageJsonFiles(files) {
|
|
36
67
|
const normalize = (filePath)=>filePath.replaceAll("\\", "/");
|
|
37
68
|
const packageJsonSet = new Set(files.map((file)=>normalize(file.relativePath)));
|
package/commonjs/src/index.js
CHANGED
|
@@ -48,9 +48,6 @@ _export(exports, {
|
|
|
48
48
|
get WorkspaceFactory () {
|
|
49
49
|
return _workspacefactory.WorkspaceFactory;
|
|
50
50
|
},
|
|
51
|
-
get findWorkspaceRoot () {
|
|
52
|
-
return _findworkspaceroot.findWorkspaceRoot;
|
|
53
|
-
},
|
|
54
51
|
get packageAliases () {
|
|
55
52
|
return _packagealiases.packageAliases;
|
|
56
53
|
},
|
|
@@ -61,7 +58,6 @@ _export(exports, {
|
|
|
61
58
|
const _const = require("./const.js");
|
|
62
59
|
const _errors = require("./errors.js");
|
|
63
60
|
const _configfactory = require("./factories/config.factory.js");
|
|
64
|
-
const _findworkspaceroot = require("./factories/find-workspace-root.js");
|
|
65
61
|
const _projectfactory = require("./factories/project.factory.js");
|
|
66
62
|
const _workspacefactory = require("./factories/workspace.factory.js");
|
|
67
63
|
const _haspackagejsonmixin = require("./models/has-package-json.mixin.js");
|
|
@@ -68,6 +68,13 @@ class Workspace extends (0, _extensibility.mixwith)(_haspackagejsonmixin.HasPack
|
|
|
68
68
|
get rootDir() {
|
|
69
69
|
return this.#config.rootDir ?? "./";
|
|
70
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* One project, whose `package.json` is the root's: a folder without `projects/`. Commands
|
|
73
|
+
* that write a project's manifest from the root's (dependency sync) or map projects to each
|
|
74
|
+
* other (paths) have nothing to do then.
|
|
75
|
+
*/ get isStandalone() {
|
|
76
|
+
return this.#projectSeeds.length === 1 && this.#projectSeeds[0]?.config?.projectDir === ".";
|
|
77
|
+
}
|
|
71
78
|
get packageJsonDirectory() {
|
|
72
79
|
return this.rootDir;
|
|
73
80
|
}
|
|
@@ -2,6 +2,7 @@ import path from "node:path";
|
|
|
2
2
|
import { io } from "@decohere/core/io";
|
|
3
3
|
import { findFiles } from "@decohere/devkit/io";
|
|
4
4
|
import { merge } from "@decohere/core/objects";
|
|
5
|
+
import { PACKAGE_JSON, PROJECTS_DIR, WorkspaceDefaults } from "../const.js";
|
|
5
6
|
import { WorkspaceConfigurationError } from "../errors.js";
|
|
6
7
|
import { PackageJson } from "../models/package-json.model.js";
|
|
7
8
|
import { Project } from "../models/project.model.js";
|
|
@@ -14,9 +15,39 @@ export class ProjectFactory {
|
|
|
14
15
|
ignore: ignoreProjects
|
|
15
16
|
});
|
|
16
17
|
const packageJsonFiles = ProjectFactory.filterNestedPackageJsonFiles(discoveredPackageJsonFiles);
|
|
18
|
+
if (packageJsonFiles.length === 0 && await ProjectFactory.isStandalone(config)) {
|
|
19
|
+
return [
|
|
20
|
+
await ProjectFactory.standaloneProject(config)
|
|
21
|
+
];
|
|
22
|
+
}
|
|
17
23
|
const projects = await Promise.all(packageJsonFiles.map((file)=>ProjectFactory.projectFrom(file, config)));
|
|
18
24
|
return projects.sort((a, b)=>a.name.localeCompare(b.name));
|
|
19
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* A standalone project: no `projects/` folder and no `includeProjects` of the user's own, but
|
|
28
|
+
* a root `package.json`. That `package.json` is then the one project, at the root.
|
|
29
|
+
*/ static async isStandalone(config) {
|
|
30
|
+
const rootDir = config.rootDir ?? "./";
|
|
31
|
+
const defaultInclude = JSON.stringify(config.includeProjects) === JSON.stringify(WorkspaceDefaults.includeProjects);
|
|
32
|
+
return defaultInclude && !await io.directoryExists(path.join(rootDir, PROJECTS_DIR)) && await io.fileExists(path.join(rootDir, PACKAGE_JSON));
|
|
33
|
+
}
|
|
34
|
+
/** The root `package.json` as the one project. Build output and `.git` aren't its sources. */ static async standaloneProject(config) {
|
|
35
|
+
const [packageJsonFile] = await findFiles([
|
|
36
|
+
PACKAGE_JSON
|
|
37
|
+
], {
|
|
38
|
+
cwd: config.rootDir ?? "./"
|
|
39
|
+
});
|
|
40
|
+
const outDir = (config.outDir ?? WorkspaceDefaults.outDir).replace(/[/\\]+$/, "");
|
|
41
|
+
const ignore = [
|
|
42
|
+
...config.ignore ?? [],
|
|
43
|
+
`${outDir}/**`,
|
|
44
|
+
".git/**"
|
|
45
|
+
];
|
|
46
|
+
return ProjectFactory.projectFrom(packageJsonFile, {
|
|
47
|
+
...config,
|
|
48
|
+
ignore
|
|
49
|
+
});
|
|
50
|
+
}
|
|
20
51
|
static filterNestedPackageJsonFiles(files) {
|
|
21
52
|
const normalize = (filePath)=>filePath.replaceAll("\\", "/");
|
|
22
53
|
const packageJsonSet = new Set(files.map((file)=>normalize(file.relativePath)));
|
package/es6/src/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
export { WORKSPACE_CONFIG, WorkspaceDefaults } from "./const.js";
|
|
2
2
|
export { WorkspaceConfigurationError } from "./errors.js";
|
|
3
3
|
export { UserConfigFactory } from "./factories/config.factory.js";
|
|
4
|
-
export { findWorkspaceRoot } from "./factories/find-workspace-root.js";
|
|
5
4
|
export { ProjectFactory } from "./factories/project.factory.js";
|
|
6
5
|
export { WorkspaceFactory } from "./factories/workspace.factory.js";
|
|
7
6
|
export { HasPackageJson } from "./models/has-package-json.mixin.js";
|
|
@@ -58,6 +58,13 @@ export class Workspace extends mixwith(HasPackageJson, HasTsConfig) {
|
|
|
58
58
|
get rootDir() {
|
|
59
59
|
return this.#config.rootDir ?? "./";
|
|
60
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* One project, whose `package.json` is the root's: a folder without `projects/`. Commands
|
|
63
|
+
* that write a project's manifest from the root's (dependency sync) or map projects to each
|
|
64
|
+
* other (paths) have nothing to do then.
|
|
65
|
+
*/ get isStandalone() {
|
|
66
|
+
return this.#projectSeeds.length === 1 && this.#projectSeeds[0]?.config?.projectDir === ".";
|
|
67
|
+
}
|
|
61
68
|
get packageJsonDirectory() {
|
|
62
69
|
return this.rootDir;
|
|
63
70
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decohere/workspace",
|
|
3
|
-
"version": "0.0.1-dev.
|
|
3
|
+
"version": "0.0.1-dev.130",
|
|
4
4
|
"description": "Workspace and project model primitives for monorepo tooling",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -33,9 +33,9 @@
|
|
|
33
33
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@decohere/core": "0.0.1-dev.
|
|
37
|
-
"@decohere/crucible": "0.0.1-dev.
|
|
38
|
-
"@decohere/devkit": "0.0.1-dev.
|
|
36
|
+
"@decohere/core": "0.0.1-dev.130",
|
|
37
|
+
"@decohere/crucible": "0.0.1-dev.130",
|
|
38
|
+
"@decohere/devkit": "0.0.1-dev.130",
|
|
39
39
|
"picomatch": "^4.0.4",
|
|
40
40
|
"rxjs": "^7.8.2",
|
|
41
41
|
"typescript": "^6.0.3"
|
|
@@ -2,6 +2,13 @@ import { Project, type ProjectSeed } from "../models/project.model.js";
|
|
|
2
2
|
import type { WorkspaceOptions } from "../models/workspace-config.type.js";
|
|
3
3
|
export declare class ProjectFactory {
|
|
4
4
|
static getProjectSeeds(config: WorkspaceOptions): Promise<ProjectSeed[]>;
|
|
5
|
+
/**
|
|
6
|
+
* A standalone project: no `projects/` folder and no `includeProjects` of the user's own, but
|
|
7
|
+
* a root `package.json`. That `package.json` is then the one project, at the root.
|
|
8
|
+
*/
|
|
9
|
+
private static isStandalone;
|
|
10
|
+
/** The root `package.json` as the one project. Build output and `.git` aren't its sources. */
|
|
11
|
+
private static standaloneProject;
|
|
5
12
|
private static filterNestedPackageJsonFiles;
|
|
6
13
|
static getProjects(config: WorkspaceOptions, configuration?: string): Promise<Project[]>;
|
|
7
14
|
private static projectFrom;
|
package/types/src/index.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
export { WORKSPACE_CONFIG, WorkspaceDefaults } from "./const.js";
|
|
2
2
|
export { WorkspaceConfigurationError } from "./errors.js";
|
|
3
3
|
export { UserConfigFactory } from "./factories/config.factory.js";
|
|
4
|
-
export { findWorkspaceRoot } from "./factories/find-workspace-root.js";
|
|
5
4
|
export { ProjectFactory } from "./factories/project.factory.js";
|
|
6
5
|
export { WorkspaceFactory } from "./factories/workspace.factory.js";
|
|
7
6
|
export { HasPackageJson } from "./models/has-package-json.mixin.js";
|
|
@@ -19,6 +19,12 @@ export declare class Workspace extends Workspace_base {
|
|
|
19
19
|
protected get projectSeeds(): readonly ProjectSeed[];
|
|
20
20
|
getProjects(configuration: string): readonly Project[];
|
|
21
21
|
get rootDir(): string;
|
|
22
|
+
/**
|
|
23
|
+
* One project, whose `package.json` is the root's: a folder without `projects/`. Commands
|
|
24
|
+
* that write a project's manifest from the root's (dependency sync) or map projects to each
|
|
25
|
+
* other (paths) have nothing to do then.
|
|
26
|
+
*/
|
|
27
|
+
get isStandalone(): boolean;
|
|
22
28
|
get packageJsonDirectory(): string;
|
|
23
29
|
protected get tsConfigDirectory(): string;
|
|
24
30
|
protected get fallbackTsConfigDirectory(): string | undefined;
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
Object.defineProperty(exports, "findWorkspaceRoot", {
|
|
6
|
-
enumerable: true,
|
|
7
|
-
get: function() {
|
|
8
|
-
return findWorkspaceRoot;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
const _nodefs = require("node:fs");
|
|
12
|
-
const _nodepath = /*#__PURE__*/ _interop_require_default(require("node:path"));
|
|
13
|
-
const _const = require("../const.js");
|
|
14
|
-
function _interop_require_default(obj) {
|
|
15
|
-
return obj && obj.__esModule ? obj : {
|
|
16
|
-
default: obj
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
function findWorkspaceRoot(startDir) {
|
|
20
|
-
const start = _nodepath.default.resolve(startDir);
|
|
21
|
-
let dir = start;
|
|
22
|
-
for(;;){
|
|
23
|
-
if ((0, _nodefs.existsSync)(_nodepath.default.join(dir, _const.PACKAGE_JSON)) && isDirectory(_nodepath.default.join(dir, _const.PROJECTS_DIR))) {
|
|
24
|
-
return dir;
|
|
25
|
-
}
|
|
26
|
-
const parent = _nodepath.default.dirname(dir);
|
|
27
|
-
if (parent === dir) return start;
|
|
28
|
-
dir = parent;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
function isDirectory(fullPath) {
|
|
32
|
-
try {
|
|
33
|
-
return (0, _nodefs.statSync)(fullPath).isDirectory();
|
|
34
|
-
} catch {
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { existsSync, statSync } from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { PACKAGE_JSON, PROJECTS_DIR } from "../const.js";
|
|
4
|
-
/**
|
|
5
|
-
* The workspace root for a command run from `startDir`: the nearest directory at or above
|
|
6
|
-
* it with a `package.json` and a `projects/` folder. Falls back to `startDir`, so running
|
|
7
|
-
* from a package directory (`projects/api`) still sees the whole workspace.
|
|
8
|
-
*/ export function findWorkspaceRoot(startDir) {
|
|
9
|
-
const start = path.resolve(startDir);
|
|
10
|
-
let dir = start;
|
|
11
|
-
for(;;){
|
|
12
|
-
if (existsSync(path.join(dir, PACKAGE_JSON)) && isDirectory(path.join(dir, PROJECTS_DIR))) {
|
|
13
|
-
return dir;
|
|
14
|
-
}
|
|
15
|
-
const parent = path.dirname(dir);
|
|
16
|
-
if (parent === dir) return start;
|
|
17
|
-
dir = parent;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
function isDirectory(fullPath) {
|
|
21
|
-
try {
|
|
22
|
-
return statSync(fullPath).isDirectory();
|
|
23
|
-
} catch {
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
@@ -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;
|