@netlify/build-info 6.2.2 → 6.3.0
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/bin.js +1 -1
- package/lib/browser/file-system.d.ts +25 -0
- package/lib/browser/file-system.js +70 -0
- package/lib/build-systems/bazel.d.ts +8 -0
- package/lib/build-systems/bazel.js +12 -0
- package/lib/build-systems/buck.d.ts +8 -0
- package/lib/build-systems/buck.js +12 -0
- package/lib/build-systems/build-system.d.ts +20 -0
- package/lib/build-systems/build-system.js +18 -0
- package/lib/build-systems/gradle.d.ts +8 -0
- package/lib/build-systems/gradle.js +12 -0
- package/lib/build-systems/index.d.ts +2 -0
- package/lib/build-systems/index.js +11 -0
- package/lib/build-systems/lage.d.ts +6 -0
- package/lib/build-systems/lage.js +6 -0
- package/lib/build-systems/moon.d.ts +8 -0
- package/lib/build-systems/moon.js +18 -0
- package/lib/build-systems/nix.d.ts +8 -0
- package/lib/build-systems/nix.js +12 -0
- package/lib/build-systems/nrwl.d.ts +11 -0
- package/lib/build-systems/nrwl.js +11 -0
- package/lib/build-systems/pants.d.ts +8 -0
- package/lib/build-systems/pants.js +12 -0
- package/lib/build-systems/rush.d.ts +6 -0
- package/lib/build-systems/rush.js +6 -0
- package/lib/build-systems/turbo.d.ts +6 -0
- package/lib/build-systems/turbo.js +6 -0
- package/lib/file-system.d.ts +69 -0
- package/lib/file-system.js +195 -0
- package/lib/index.d.ts +2 -1
- package/lib/index.js +2 -1
- package/lib/{bin.d.ts → node/bin.d.ts} +0 -0
- package/lib/{bin.js → node/bin.js} +2 -3
- package/lib/node/file-system.d.ts +19 -0
- package/lib/node/file-system.js +65 -0
- package/lib/node/get-build-info.d.ts +20 -0
- package/lib/node/get-build-info.js +46 -0
- package/lib/{detect-package-manager.d.ts → package-managers/detect-package-manager.d.ts} +2 -4
- package/lib/package-managers/detect-package-manager.js +75 -0
- package/lib/project.d.ts +33 -0
- package/lib/project.js +82 -0
- package/lib/workspaces/detect-workspace.d.ts +22 -0
- package/lib/workspaces/detect-workspace.js +56 -0
- package/lib/workspaces/get-workspace-packages.d.ts +3 -0
- package/lib/workspaces/get-workspace-packages.js +66 -0
- package/package.json +9 -4
- package/lib/context.d.ts +0 -11
- package/lib/context.js +0 -45
- package/lib/detect-build-system.d.ts +0 -5
- package/lib/detect-build-system.js +0 -145
- package/lib/detect-package-manager.js +0 -71
- package/lib/get-build-info.d.ts +0 -11
- package/lib/get-build-info.js +0 -34
- package/lib/workspaces.d.ts +0 -22
- package/lib/workspaces.js +0 -56
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { argv, exit } from 'process';
|
|
2
2
|
import yargs from 'yargs';
|
|
3
3
|
import { hideBin } from 'yargs/helpers';
|
|
4
4
|
import { getBuildInfo } from './get-build-info.js';
|
|
@@ -10,8 +10,7 @@ yargs(hideBin(argv))
|
|
|
10
10
|
},
|
|
11
11
|
}), async ({ projectDir, rootDir }) => {
|
|
12
12
|
try {
|
|
13
|
-
|
|
14
|
-
console.log(JSON.stringify(buildInfo, null, 2));
|
|
13
|
+
console.log(JSON.stringify(await getBuildInfo(projectDir, rootDir), null, 2));
|
|
15
14
|
}
|
|
16
15
|
catch (error) {
|
|
17
16
|
console.error(error);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { DirType, FileSystem, findUpOptions } from '../file-system.js';
|
|
2
|
+
export declare class NodeFS extends FileSystem {
|
|
3
|
+
constructor();
|
|
4
|
+
isAbsolute(path: string): boolean;
|
|
5
|
+
dirname(path: string): string;
|
|
6
|
+
resolve(...paths: string[]): string;
|
|
7
|
+
relative(from: string, to: string): string;
|
|
8
|
+
basename(path: string): string;
|
|
9
|
+
join(...segments: string[]): string;
|
|
10
|
+
fileExists(path: string): Promise<boolean>;
|
|
11
|
+
/** Get a list of directory entries */
|
|
12
|
+
readDir(path: string): Promise<string[]>;
|
|
13
|
+
readDir(path: string, withFileTypes: true): Promise<Record<string, DirType>>;
|
|
14
|
+
readFile(path: string): Promise<string>;
|
|
15
|
+
/** Node implementation of finding a file or directory by walking up parent directories. */
|
|
16
|
+
findUp(name: string | readonly string[], options?: findUpOptions): Promise<string | undefined>;
|
|
17
|
+
/** Node implementation of finding files or directories by walking up parent directories. */
|
|
18
|
+
findUpMultiple(name: string | readonly string[], options?: findUpOptions): Promise<string[]>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { promises as fs } from 'fs';
|
|
2
|
+
import { basename, dirname, isAbsolute, join, relative, resolve } from 'path';
|
|
3
|
+
import { findUp, findUpMultiple } from 'find-up';
|
|
4
|
+
import { FileSystem } from '../file-system.js';
|
|
5
|
+
export class NodeFS extends FileSystem {
|
|
6
|
+
constructor() {
|
|
7
|
+
super();
|
|
8
|
+
this.cwd = process.cwd();
|
|
9
|
+
}
|
|
10
|
+
isAbsolute(path) {
|
|
11
|
+
return isAbsolute(path);
|
|
12
|
+
}
|
|
13
|
+
dirname(path) {
|
|
14
|
+
return dirname(path);
|
|
15
|
+
}
|
|
16
|
+
resolve(...paths) {
|
|
17
|
+
return resolve(...paths);
|
|
18
|
+
}
|
|
19
|
+
relative(from, to) {
|
|
20
|
+
return relative(from, to);
|
|
21
|
+
}
|
|
22
|
+
basename(path) {
|
|
23
|
+
return basename(path);
|
|
24
|
+
}
|
|
25
|
+
join(...segments) {
|
|
26
|
+
return join(...segments);
|
|
27
|
+
}
|
|
28
|
+
async fileExists(path) {
|
|
29
|
+
try {
|
|
30
|
+
await fs.stat(resolve(path));
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async readDir(path, withFileTypes) {
|
|
38
|
+
try {
|
|
39
|
+
if (!withFileTypes) {
|
|
40
|
+
return fs.readdir(resolve(path));
|
|
41
|
+
}
|
|
42
|
+
const result = await fs.readdir(resolve(path), { withFileTypes: true });
|
|
43
|
+
return result.reduce((prev, cur) => ({ ...prev, [cur.name]: cur.isDirectory() ? 'directory' : 'file' }), {});
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async readFile(path) {
|
|
50
|
+
const storedFile = this.getFile(path);
|
|
51
|
+
// no need to read again (just use the already stored file)
|
|
52
|
+
if (storedFile?.type === 'text') {
|
|
53
|
+
return storedFile.content;
|
|
54
|
+
}
|
|
55
|
+
return (await fs.readFile(resolve(path), 'utf-8')).toString();
|
|
56
|
+
}
|
|
57
|
+
/** Node implementation of finding a file or directory by walking up parent directories. */
|
|
58
|
+
findUp(name, options = {}) {
|
|
59
|
+
return findUp(name, options);
|
|
60
|
+
}
|
|
61
|
+
/** Node implementation of finding files or directories by walking up parent directories. */
|
|
62
|
+
findUpMultiple(name, options = {}) {
|
|
63
|
+
return findUpMultiple(name, options);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Logger } from '../file-system.js';
|
|
2
|
+
import { PkgManagerFields } from '../package-managers/detect-package-manager.js';
|
|
3
|
+
import { WorkspaceInfo } from '../workspaces/detect-workspace.js';
|
|
4
|
+
export type Info = {
|
|
5
|
+
jsWorkspaces?: WorkspaceInfo;
|
|
6
|
+
packageManager?: PkgManagerFields;
|
|
7
|
+
frameworks: unknown[];
|
|
8
|
+
buildSystems?: {
|
|
9
|
+
name: string;
|
|
10
|
+
version?: string | undefined;
|
|
11
|
+
}[];
|
|
12
|
+
};
|
|
13
|
+
/** A noop logger that is used to not log anything (we use the stdout for parsing the json output) */
|
|
14
|
+
export declare class NoopLogger implements Logger {
|
|
15
|
+
debug(): void;
|
|
16
|
+
log(): void;
|
|
17
|
+
error(): void;
|
|
18
|
+
}
|
|
19
|
+
/** Get the build info object that is used inside buildbot */
|
|
20
|
+
export declare function getBuildInfo(projectDir?: string, rootDir?: string): Promise<Info>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { listFrameworks } from '@netlify/framework-info';
|
|
2
|
+
import { Project } from '../project.js';
|
|
3
|
+
import { NodeFS } from './file-system.js';
|
|
4
|
+
/** A noop logger that is used to not log anything (we use the stdout for parsing the json output) */
|
|
5
|
+
export class NoopLogger {
|
|
6
|
+
debug() {
|
|
7
|
+
/** noop */
|
|
8
|
+
}
|
|
9
|
+
log() {
|
|
10
|
+
/** noop */
|
|
11
|
+
}
|
|
12
|
+
error() {
|
|
13
|
+
/** noop */
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/** Get the build info object that is used inside buildbot */
|
|
17
|
+
export async function getBuildInfo(projectDir, rootDir) {
|
|
18
|
+
const fs = new NodeFS();
|
|
19
|
+
// prevent logging in output as we use the stdout to capture the json
|
|
20
|
+
fs.logger = new NoopLogger();
|
|
21
|
+
const project = new Project(fs, projectDir, rootDir);
|
|
22
|
+
project.setEnvironment(process.env);
|
|
23
|
+
let frameworks = [];
|
|
24
|
+
try {
|
|
25
|
+
// if the framework detection is crashing we should not crash the build info and package-manager detection
|
|
26
|
+
frameworks = await listFrameworks({ projectDir: project.baseDirectory });
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// TODO: build reporting to buildbot see: https://github.com/netlify/pillar-workflow/issues/1001
|
|
30
|
+
// noop
|
|
31
|
+
}
|
|
32
|
+
const info = {
|
|
33
|
+
frameworks,
|
|
34
|
+
buildSystems: await project.detectBuildSystem(),
|
|
35
|
+
};
|
|
36
|
+
const pkgJSONPath = await project.getPackageJSON();
|
|
37
|
+
// only if we find a root package.json we know this is a javascript workspace
|
|
38
|
+
if (Object.keys(pkgJSONPath).length) {
|
|
39
|
+
info.packageManager = await project.detectPackageManager();
|
|
40
|
+
const workspaceInfo = await project.detectWorkspaces();
|
|
41
|
+
if (workspaceInfo) {
|
|
42
|
+
info.jsWorkspaces = workspaceInfo;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return info;
|
|
46
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Project } from '../project.js';
|
|
1
2
|
export declare const enum PkgManager {
|
|
2
3
|
YARN = "yarn",
|
|
3
4
|
PNPM = "pnpm",
|
|
@@ -22,8 +23,5 @@ export type PkgManagerFields = {
|
|
|
22
23
|
* 1. packageManager field
|
|
23
24
|
* 2. environment variable that forces the usage
|
|
24
25
|
* 3. a lock file that is present in this directory or up in the tree for workspaces
|
|
25
|
-
* @param cwd The current process working directory of the build
|
|
26
|
-
* @param stopAt The repository root where it should stop looking up for package.json or lock files. (defaults to `path.parse(cwd).root`)
|
|
27
|
-
* @returns The package manager that was detected
|
|
28
26
|
*/
|
|
29
|
-
export declare const detectPackageManager: (
|
|
27
|
+
export declare const detectPackageManager: (project: Project) => Promise<PkgManagerFields>;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/** The definition of all available package managers */
|
|
2
|
+
const AVAILABLE_PACKAGE_MANAGERS = {
|
|
3
|
+
["yarn" /* PkgManager.YARN */]: {
|
|
4
|
+
name: "yarn" /* PkgManager.YARN */,
|
|
5
|
+
installCommand: 'yarn install',
|
|
6
|
+
lockFile: 'yarn.lock',
|
|
7
|
+
forceEnvironment: 'NETLIFY_USE_YARN',
|
|
8
|
+
},
|
|
9
|
+
["pnpm" /* PkgManager.PNPM */]: {
|
|
10
|
+
name: "pnpm" /* PkgManager.PNPM */,
|
|
11
|
+
installCommand: 'pnpm install',
|
|
12
|
+
lockFile: 'pnpm-lock.yaml',
|
|
13
|
+
forceEnvironment: 'NETLIFY_USE_PNPM',
|
|
14
|
+
},
|
|
15
|
+
["npm" /* PkgManager.NPM */]: {
|
|
16
|
+
name: "npm" /* PkgManager.NPM */,
|
|
17
|
+
installCommand: 'npm install',
|
|
18
|
+
lockFile: 'package-lock.json',
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* generate a map out of key is lock file and value the package manager
|
|
23
|
+
* this is to reduce the complexity in loops
|
|
24
|
+
*/
|
|
25
|
+
const lockFileMap = Object.values(AVAILABLE_PACKAGE_MANAGERS).reduce((cur, pkgManager) => ({ ...cur, [pkgManager.lockFile]: pkgManager }), {});
|
|
26
|
+
/**
|
|
27
|
+
* Detects the used package manager based on
|
|
28
|
+
* 1. packageManager field
|
|
29
|
+
* 2. environment variable that forces the usage
|
|
30
|
+
* 3. a lock file that is present in this directory or up in the tree for workspaces
|
|
31
|
+
*/
|
|
32
|
+
export const detectPackageManager = async (project) => {
|
|
33
|
+
try {
|
|
34
|
+
const pkgPaths = await project.fs.findUpMultiple('package.json', {
|
|
35
|
+
cwd: project.baseDirectory,
|
|
36
|
+
stopAt: project.root,
|
|
37
|
+
});
|
|
38
|
+
for (const pkgPath of pkgPaths) {
|
|
39
|
+
const { packageManager } = await project.fs.readJSON(pkgPath);
|
|
40
|
+
if (packageManager) {
|
|
41
|
+
const [parsed] = packageManager.split('@');
|
|
42
|
+
if (AVAILABLE_PACKAGE_MANAGERS[parsed]) {
|
|
43
|
+
return AVAILABLE_PACKAGE_MANAGERS[parsed];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// the package manager can be enforced via an environment variable as well
|
|
48
|
+
for (const pkgManager of Object.values(AVAILABLE_PACKAGE_MANAGERS)) {
|
|
49
|
+
if (pkgManager.forceEnvironment && project.getEnv(pkgManager.forceEnvironment) === 'true') {
|
|
50
|
+
return pkgManager;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// find the correct lock file the tree up
|
|
54
|
+
const lockFilePath = await project.fs.findUp(Object.keys(lockFileMap), {
|
|
55
|
+
cwd: project.baseDirectory,
|
|
56
|
+
stopAt: project.root,
|
|
57
|
+
});
|
|
58
|
+
// if we found a lock file and the usage is not prohibited through an environment variable
|
|
59
|
+
// return the found package manager
|
|
60
|
+
if (lockFilePath) {
|
|
61
|
+
const lockFile = project.fs.basename(lockFilePath);
|
|
62
|
+
const pkgManager = lockFileMap[lockFile];
|
|
63
|
+
// check if it not got disabled
|
|
64
|
+
if (!(pkgManager.forceEnvironment && project.getEnv(pkgManager.forceEnvironment) === 'false')) {
|
|
65
|
+
return pkgManager;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// noop
|
|
71
|
+
}
|
|
72
|
+
// always default to npm
|
|
73
|
+
// TODO: add some reporting here to log that we fall backed
|
|
74
|
+
return AVAILABLE_PACKAGE_MANAGERS["npm" /* PkgManager.NPM */];
|
|
75
|
+
};
|
package/lib/project.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { PackageJson } from 'read-pkg';
|
|
2
|
+
import type { BuildSystem } from './build-systems/build-system.js';
|
|
3
|
+
import type { FileSystem } from './file-system.js';
|
|
4
|
+
import type { PkgManagerFields } from './package-managers/detect-package-manager.js';
|
|
5
|
+
/**
|
|
6
|
+
* The Project represents a Site in Netlify
|
|
7
|
+
* The only configuration here needed is the path to the repository root and the optional baseDirectory
|
|
8
|
+
*/
|
|
9
|
+
export declare class Project {
|
|
10
|
+
#private;
|
|
11
|
+
fs: FileSystem;
|
|
12
|
+
/** An optional repository root that tells us where to stop looking up */
|
|
13
|
+
root?: string;
|
|
14
|
+
/** An absolute path */
|
|
15
|
+
baseDirectory: string;
|
|
16
|
+
packageManager: PkgManagerFields;
|
|
17
|
+
/** an absolute path of the root directory for js workspaces where the most top package.json is located */
|
|
18
|
+
jsWorkspaceRoot: string;
|
|
19
|
+
constructor(fs: FileSystem, baseDirectory?: string, root?: string);
|
|
20
|
+
/** Set's the environment for the project */
|
|
21
|
+
setEnvironment(env: Record<string, string | undefined>): this;
|
|
22
|
+
/** retrieves an environment variable */
|
|
23
|
+
getEnv(key: string): string | undefined;
|
|
24
|
+
/** retrieves the root package.json file */
|
|
25
|
+
getRootPackageJSON(): Promise<PackageJson>;
|
|
26
|
+
getPackageJSON(startDirectory?: string): Promise<PackageJson>;
|
|
27
|
+
/** Detects the used package Manager */
|
|
28
|
+
detectPackageManager(): Promise<PkgManagerFields>;
|
|
29
|
+
/** Detects the javascript workspace settings */
|
|
30
|
+
detectWorkspaces(): Promise<import("./workspaces/detect-workspace.js").WorkspaceInfo | undefined>;
|
|
31
|
+
/** Detects all used build systems */
|
|
32
|
+
detectBuildSystem(): Promise<BuildSystem[]>;
|
|
33
|
+
}
|
package/lib/project.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { buildSystems } from './build-systems/index.js';
|
|
2
|
+
import { detectPackageManager } from './package-managers/detect-package-manager.js';
|
|
3
|
+
import { detectWorkspaces } from './workspaces/detect-workspace.js';
|
|
4
|
+
/**
|
|
5
|
+
* The Project represents a Site in Netlify
|
|
6
|
+
* The only configuration here needed is the path to the repository root and the optional baseDirectory
|
|
7
|
+
*/
|
|
8
|
+
export class Project {
|
|
9
|
+
fs;
|
|
10
|
+
/** An optional repository root that tells us where to stop looking up */
|
|
11
|
+
root;
|
|
12
|
+
/** An absolute path */
|
|
13
|
+
baseDirectory;
|
|
14
|
+
packageManager;
|
|
15
|
+
/** an absolute path of the root directory for js workspaces where the most top package.json is located */
|
|
16
|
+
jsWorkspaceRoot;
|
|
17
|
+
/** a representation of the current environment */
|
|
18
|
+
#environment = {};
|
|
19
|
+
constructor(fs, baseDirectory, root) {
|
|
20
|
+
this.fs = fs;
|
|
21
|
+
this.baseDirectory = fs.resolve(root || '', baseDirectory !== undefined ? baseDirectory : fs.cwd);
|
|
22
|
+
this.root = root ? fs.resolve(fs.cwd, root) : undefined;
|
|
23
|
+
// if the root and the base directory are the same unset the root again as its not a workspace
|
|
24
|
+
if (this.root === this.baseDirectory) {
|
|
25
|
+
this.root = undefined;
|
|
26
|
+
}
|
|
27
|
+
this.fs.cwd = this.baseDirectory;
|
|
28
|
+
}
|
|
29
|
+
/** Set's the environment for the project */
|
|
30
|
+
setEnvironment(env) {
|
|
31
|
+
this.#environment = env;
|
|
32
|
+
return this;
|
|
33
|
+
}
|
|
34
|
+
/** retrieves an environment variable */
|
|
35
|
+
getEnv(key) {
|
|
36
|
+
return this.#environment[key];
|
|
37
|
+
}
|
|
38
|
+
/** retrieves the root package.json file */
|
|
39
|
+
async getRootPackageJSON() {
|
|
40
|
+
// get the most upper json file
|
|
41
|
+
const rootJSONPath = (await this.fs.findUpMultiple('package.json', { cwd: this.baseDirectory, stopAt: this.root })).pop();
|
|
42
|
+
if (rootJSONPath) {
|
|
43
|
+
this.jsWorkspaceRoot = this.fs.dirname(rootJSONPath);
|
|
44
|
+
return this.fs.readJSON(rootJSONPath);
|
|
45
|
+
}
|
|
46
|
+
return {};
|
|
47
|
+
}
|
|
48
|
+
async getPackageJSON(startDirectory) {
|
|
49
|
+
const pkgPath = await this.fs.findUp('package.json', {
|
|
50
|
+
cwd: startDirectory || this.baseDirectory,
|
|
51
|
+
stopAt: this.root,
|
|
52
|
+
});
|
|
53
|
+
if (pkgPath) {
|
|
54
|
+
return this.fs.readJSON(pkgPath);
|
|
55
|
+
}
|
|
56
|
+
return {};
|
|
57
|
+
}
|
|
58
|
+
/** Detects the used package Manager */
|
|
59
|
+
async detectPackageManager() {
|
|
60
|
+
this.packageManager = await detectPackageManager(this);
|
|
61
|
+
return this.packageManager;
|
|
62
|
+
}
|
|
63
|
+
/** Detects the javascript workspace settings */
|
|
64
|
+
async detectWorkspaces() {
|
|
65
|
+
try {
|
|
66
|
+
return detectWorkspaces(this);
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** Detects all used build systems */
|
|
73
|
+
async detectBuildSystem() {
|
|
74
|
+
try {
|
|
75
|
+
const detected = await Promise.all(buildSystems.map(async (BuildSystem) => (await new BuildSystem().detect(this))?.toJSON()));
|
|
76
|
+
return detected.filter(Boolean);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { PackageJson } from 'read-pkg';
|
|
2
|
+
import { Project } from '../project.js';
|
|
3
|
+
export type WorkspaceInfo = {
|
|
4
|
+
/** if we are in the current workspace root or not */
|
|
5
|
+
isRoot: boolean;
|
|
6
|
+
/** the workspace root directory */
|
|
7
|
+
rootDir: string;
|
|
8
|
+
/** list of relative package paths inside the workspace */
|
|
9
|
+
packages: string[];
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Get a list of globs about all the packages inside a pnpm workspace
|
|
13
|
+
* https://pnpm.io/pnpm-workspace_yaml
|
|
14
|
+
*/
|
|
15
|
+
export declare function detectPnpmWorkspaceGlobs(project: Project): Promise<string[]>;
|
|
16
|
+
/** Get the workspace globs from the package.json file */
|
|
17
|
+
export declare function detectNpmOrYarnWorkspaceGlobs(pkgJSON: PackageJson): Promise<string[]>;
|
|
18
|
+
/**
|
|
19
|
+
* If it's a javascript workspace (npm, pnpm, yarn) it will retrieve a list of all
|
|
20
|
+
* package paths and will indicate if it's the root of the workspace
|
|
21
|
+
*/
|
|
22
|
+
export declare function detectWorkspaces(project: Project): Promise<WorkspaceInfo | undefined>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { parse } from 'yaml';
|
|
2
|
+
import { getWorkspacePackages } from './get-workspace-packages.js';
|
|
3
|
+
/**
|
|
4
|
+
* Get a list of globs about all the packages inside a pnpm workspace
|
|
5
|
+
* https://pnpm.io/pnpm-workspace_yaml
|
|
6
|
+
*/
|
|
7
|
+
export async function detectPnpmWorkspaceGlobs(project) {
|
|
8
|
+
const workspaceFile = await project.fs.findUp('pnpm-workspace.yaml', {
|
|
9
|
+
cwd: project.baseDirectory,
|
|
10
|
+
stopAt: project.root,
|
|
11
|
+
});
|
|
12
|
+
if (!workspaceFile) {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
const { packages } = parse(await project.fs.readFile(workspaceFile));
|
|
16
|
+
return packages;
|
|
17
|
+
}
|
|
18
|
+
/** Get the workspace globs from the package.json file */
|
|
19
|
+
export async function detectNpmOrYarnWorkspaceGlobs(pkgJSON) {
|
|
20
|
+
if (Array.isArray(pkgJSON.workspaces)) {
|
|
21
|
+
return pkgJSON.workspaces;
|
|
22
|
+
}
|
|
23
|
+
if (typeof pkgJSON.workspaces === 'object') {
|
|
24
|
+
return pkgJSON.workspaces.packages || [];
|
|
25
|
+
}
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* If it's a javascript workspace (npm, pnpm, yarn) it will retrieve a list of all
|
|
30
|
+
* package paths and will indicate if it's the root of the workspace
|
|
31
|
+
*/
|
|
32
|
+
export async function detectWorkspaces(project) {
|
|
33
|
+
if (!project.packageManager) {
|
|
34
|
+
throw new Error('Please run the packageManager detection before calling the workspace detection!');
|
|
35
|
+
}
|
|
36
|
+
const pkgJSON = await project.getRootPackageJSON();
|
|
37
|
+
const workspaceGlobs = project.packageManager.name === "pnpm" /* PkgManager.PNPM */
|
|
38
|
+
? await detectPnpmWorkspaceGlobs(project)
|
|
39
|
+
: await detectNpmOrYarnWorkspaceGlobs(pkgJSON);
|
|
40
|
+
if (workspaceGlobs.length === 0) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const packages = await getWorkspacePackages(project, workspaceGlobs);
|
|
44
|
+
const isRoot = project.baseDirectory === project.jsWorkspaceRoot;
|
|
45
|
+
const relBaseDirectory = project.fs.relative(project.jsWorkspaceRoot, project.baseDirectory);
|
|
46
|
+
// if the current base directory is not part of the detected workspace packages it's not part of this workspace
|
|
47
|
+
// and therefore return no workspace info
|
|
48
|
+
if (!isRoot && !packages.includes(relBaseDirectory)) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
rootDir: project.jsWorkspaceRoot,
|
|
53
|
+
isRoot,
|
|
54
|
+
packages,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import minimatch, { Minimatch } from 'minimatch';
|
|
2
|
+
/** Get the base directory out of a glob pattern */
|
|
3
|
+
function getDirFromPattern(pattern) {
|
|
4
|
+
const dir = [];
|
|
5
|
+
const parts = pattern.globParts[0];
|
|
6
|
+
for (let i = 0, max = parts.length; i < max; i++) {
|
|
7
|
+
if (parts[i].includes('*')) {
|
|
8
|
+
// skip the rest
|
|
9
|
+
return { dir: dir.join('/'), depth: parts[i] };
|
|
10
|
+
}
|
|
11
|
+
dir.push(parts[i]);
|
|
12
|
+
}
|
|
13
|
+
return { dir: dir.join('/') };
|
|
14
|
+
}
|
|
15
|
+
/** Find all packages inside a provided directory */
|
|
16
|
+
async function findPackages(project, dir, depth) {
|
|
17
|
+
const found = [];
|
|
18
|
+
let content = {};
|
|
19
|
+
try {
|
|
20
|
+
const startDir = project.root ? project.fs.resolve(project.root, dir) : project.fs.resolve(dir);
|
|
21
|
+
content = await project.fs.readDir(startDir, true);
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
// noop
|
|
25
|
+
}
|
|
26
|
+
for (const [part, type] of Object.entries(content)) {
|
|
27
|
+
if (part === 'package.json') {
|
|
28
|
+
found.push(dir);
|
|
29
|
+
}
|
|
30
|
+
if (depth && type === 'directory') {
|
|
31
|
+
found.push(...(await findPackages(project, project.fs.join(dir, part), depth === '**' ? depth : undefined)));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return found;
|
|
35
|
+
}
|
|
36
|
+
/** Get a list of all workspace package paths (absolute paths) */
|
|
37
|
+
export async function getWorkspacePackages(project, patterns) {
|
|
38
|
+
const results = [];
|
|
39
|
+
if (!patterns.length) {
|
|
40
|
+
return results;
|
|
41
|
+
}
|
|
42
|
+
for (const pattern of patterns) {
|
|
43
|
+
const matcher = new Minimatch(pattern);
|
|
44
|
+
// skip negated pattern for exploring as we filter at the end
|
|
45
|
+
if (matcher.negate) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
const { dir, depth } = getDirFromPattern(matcher);
|
|
49
|
+
results.push(...(await findPackages(project, dir, depth)));
|
|
50
|
+
}
|
|
51
|
+
// initially add all results to the set
|
|
52
|
+
// and filter out then the negated patterns
|
|
53
|
+
const filtered = new Set();
|
|
54
|
+
for (const result of results) {
|
|
55
|
+
for (const pattern of patterns) {
|
|
56
|
+
const matcher = new Minimatch(pattern);
|
|
57
|
+
if (minimatch(result, matcher.pattern)) {
|
|
58
|
+
filtered.add(project.fs.join(result));
|
|
59
|
+
if (matcher.negate) {
|
|
60
|
+
filtered.delete(project.fs.join(result));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return [...filtered];
|
|
66
|
+
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@netlify/build-info",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.3.0",
|
|
4
4
|
"description": "Build info utility",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": "./lib/index.js"
|
|
8
8
|
},
|
|
9
|
+
"browser": "./lib/index.js",
|
|
9
10
|
"main": "./lib/index.js",
|
|
10
11
|
"types": "./lib/index.d.ts",
|
|
11
12
|
"bin": {
|
|
@@ -18,6 +19,7 @@
|
|
|
18
19
|
"scripts": {
|
|
19
20
|
"prebuild": "rm -rf lib",
|
|
20
21
|
"build": "tsc",
|
|
22
|
+
"e2e": "playwright test",
|
|
21
23
|
"test": "vitest run",
|
|
22
24
|
"test:dev": "vitest --ui",
|
|
23
25
|
"test:ci": "vitest run --reporter=default"
|
|
@@ -34,27 +36,30 @@
|
|
|
34
36
|
},
|
|
35
37
|
"author": "Netlify Inc.",
|
|
36
38
|
"dependencies": {
|
|
37
|
-
"@netlify/framework-info": "^9.8.
|
|
38
|
-
"@npmcli/map-workspaces": "^2.0.0",
|
|
39
|
+
"@netlify/framework-info": "^9.8.2",
|
|
39
40
|
"find-up": "^6.3.0",
|
|
41
|
+
"minimatch": "^6.2.0",
|
|
40
42
|
"read-pkg": "^7.1.0",
|
|
41
43
|
"yaml": "^2.1.3",
|
|
42
44
|
"yargs": "^17.6.0"
|
|
43
45
|
},
|
|
44
46
|
"devDependencies": {
|
|
47
|
+
"@playwright/test": "^1.30.0",
|
|
45
48
|
"@types/node": "^14.18.31",
|
|
46
49
|
"@types/semver": "^7.3.13",
|
|
47
50
|
"@vitest/coverage-c8": "^0.24.1",
|
|
48
51
|
"@vitest/ui": "^0.24.3",
|
|
49
52
|
"execa": "^6.0.0",
|
|
50
53
|
"memfs": "^3.4.7",
|
|
54
|
+
"node-fetch": "^3.3.0",
|
|
51
55
|
"semver": "^7.3.8",
|
|
52
56
|
"typescript": "^4.8.4",
|
|
53
57
|
"unionfs": "^4.4.0",
|
|
58
|
+
"vite": "^4.1.1",
|
|
54
59
|
"vitest": "^0.24.1"
|
|
55
60
|
},
|
|
56
61
|
"engines": {
|
|
57
62
|
"node": "^14.16.0 || >=16.0.0"
|
|
58
63
|
},
|
|
59
|
-
"gitHead": "
|
|
64
|
+
"gitHead": "ade6e389937eb921779b5af7268547a59741de5a"
|
|
60
65
|
}
|
package/lib/context.d.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { PackageJson } from 'read-pkg';
|
|
2
|
-
export type ContextOptions = {
|
|
3
|
-
projectDir?: string;
|
|
4
|
-
rootDir?: string;
|
|
5
|
-
};
|
|
6
|
-
export type Context = {
|
|
7
|
-
projectDir: string;
|
|
8
|
-
rootDir?: string;
|
|
9
|
-
rootPackageJson: PackageJson;
|
|
10
|
-
};
|
|
11
|
-
export declare const getContext: (config?: ContextOptions) => Promise<Context>;
|
package/lib/context.js
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { existsSync } from 'fs';
|
|
2
|
-
import { dirname, join, resolve } from 'path';
|
|
3
|
-
import { cwd } from 'process';
|
|
4
|
-
import { findUp } from 'find-up';
|
|
5
|
-
import { readPackage } from 'read-pkg';
|
|
6
|
-
/**
|
|
7
|
-
* Collects the root package.json if there is one.
|
|
8
|
-
*/
|
|
9
|
-
const getRootPackageJson = async (projectDir, rootDir) => {
|
|
10
|
-
if (existsSync(join(rootDir, 'package.json'))) {
|
|
11
|
-
return await readPackage({ cwd: rootDir, normalize: false });
|
|
12
|
-
}
|
|
13
|
-
const json = await findUp('package.json', { cwd: projectDir, stopAt: rootDir });
|
|
14
|
-
if (json) {
|
|
15
|
-
return await readPackage({ cwd: dirname(json), normalize: false });
|
|
16
|
-
}
|
|
17
|
-
return {};
|
|
18
|
-
};
|
|
19
|
-
export const getContext = async (config = {}) => {
|
|
20
|
-
const { projectDir = cwd(), rootDir = '' } = config;
|
|
21
|
-
// Get the absolute dirs for both project and root
|
|
22
|
-
// We resolve the projectDir from the rootDir
|
|
23
|
-
const absoluteProjectDir = resolve(rootDir, projectDir);
|
|
24
|
-
// If a relative absolute path is given we rely on cwd
|
|
25
|
-
const absoluteRootDir = rootDir ? resolve(cwd(), rootDir) : undefined;
|
|
26
|
-
// We only pass through the root dir if it was provided and is actually different
|
|
27
|
-
// from the project dir
|
|
28
|
-
const validRootDir = absoluteRootDir && absoluteRootDir !== absoluteProjectDir ? absoluteRootDir : undefined;
|
|
29
|
-
// If rootDir equals projectDir we'll be getting the projectDir package.json
|
|
30
|
-
// Later on if we also need the projectDir package.json we can check for it
|
|
31
|
-
// and only perform one resolution
|
|
32
|
-
let rootPackageJson = {};
|
|
33
|
-
try {
|
|
34
|
-
rootPackageJson = await getRootPackageJson(absoluteProjectDir, validRootDir || absoluteProjectDir);
|
|
35
|
-
}
|
|
36
|
-
catch {
|
|
37
|
-
// gracefully handle un-parseable package.json files
|
|
38
|
-
//noop
|
|
39
|
-
}
|
|
40
|
-
return {
|
|
41
|
-
projectDir: absoluteProjectDir,
|
|
42
|
-
rootDir: validRootDir,
|
|
43
|
-
rootPackageJson,
|
|
44
|
-
};
|
|
45
|
-
};
|