@arcgis/node-toolkit 5.2.0-next.31
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/LICENSE.md +17 -0
- package/README.md +12 -0
- package/dist/file.d.ts +35 -0
- package/dist/file.js +113 -0
- package/dist/glob.d.ts +24 -0
- package/dist/glob.js +28 -0
- package/dist/packageJson.d.ts +64 -0
- package/dist/packageJson.js +73 -0
- package/dist/path.d.ts +40 -0
- package/dist/path.js +48 -0
- package/dist/vite/externalizeDependenciesPlugin.d.ts +91 -0
- package/dist/vite/externalizeDependenciesPlugin.js +104 -0
- package/dist/vite/presetPlugin.d.ts +13 -0
- package/dist/vite/presetPlugin.js +76 -0
- package/package.json +26 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Licensing
|
|
2
|
+
|
|
3
|
+
COPYRIGHT © Esri
|
|
4
|
+
|
|
5
|
+
All rights reserved under the copyright laws of the United States and applicable international laws, treaties, and conventions.
|
|
6
|
+
|
|
7
|
+
This material is licensed for use under the [Esri Master License Agreement (MLA)](https://www.esri.com/content/dam/esrisites/en-us/media/legal/ma-full/ma-full.pdf), and is bound by the terms of that agreement.
|
|
8
|
+
You may redistribute and use this code without modification, provided you adhere to the terms of the MLA and include this copyright notice.
|
|
9
|
+
|
|
10
|
+
For additional information, contact:
|
|
11
|
+
Environmental Systems Research Institute, Inc.
|
|
12
|
+
Attn: Contracts and Legal Services Department
|
|
13
|
+
380 New York Street
|
|
14
|
+
Redlands, California, USA 92373
|
|
15
|
+
USA
|
|
16
|
+
|
|
17
|
+
email: legal@esri.com
|
package/README.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# ArcGIS Maps SDK for JavaScript - Node Utils
|
|
2
|
+
|
|
3
|
+
**No Esri Technical Support included.**
|
|
4
|
+
|
|
5
|
+
Package that is part of the [ArcGIS Maps SDK for JavaScript](https://developers.arcgis.com/javascript).
|
|
6
|
+
|
|
7
|
+
It is not intended to be used directly, but rather used as a dependency by other packages in the SDK.
|
|
8
|
+
|
|
9
|
+
## License
|
|
10
|
+
|
|
11
|
+
This package is licensed under the terms described in the `LICENSE.md` file, located in the root of the package, and at https://js.arcgis.com/5.1/LICENSE.txt.
|
|
12
|
+
For third party notices, see https://js.arcgis.com/5.1/third-party-notices.txt.
|
package/dist/file.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { ExecSyncOptionsWithStringEncoding, SpawnSyncOptionsWithStringEncoding } from 'node:child_process';
|
|
2
|
+
/**
|
|
3
|
+
* Asynchronously check if a file or directory exists.
|
|
4
|
+
* Using un-promisified version because promises version creates exceptions
|
|
5
|
+
* which interferes with debugging when "Pause on caught exceptions" is enabled
|
|
6
|
+
*/
|
|
7
|
+
export declare const existsAsync: (file: string) => Promise<boolean>;
|
|
8
|
+
/**
|
|
9
|
+
* Synchronously execute a shell command and return the output.
|
|
10
|
+
*/
|
|
11
|
+
export declare function sh(command: string, options?: Partial<ExecSyncOptionsWithStringEncoding>): string;
|
|
12
|
+
/**
|
|
13
|
+
* Asynchronously execute a shell command and return the output.
|
|
14
|
+
*/
|
|
15
|
+
export declare function asyncSh(command: string, options?: Partial<ExecSyncOptionsWithStringEncoding>): Promise<string>;
|
|
16
|
+
/**
|
|
17
|
+
* Synchronously execute a command without shell interpolation and return the output.
|
|
18
|
+
*/
|
|
19
|
+
export declare function sp(command: string, args: string[], options?: Partial<SpawnSyncOptionsWithStringEncoding>): string;
|
|
20
|
+
/**
|
|
21
|
+
* Create a file with the specified content if it does not already exist.
|
|
22
|
+
*/
|
|
23
|
+
export declare function createFileIfNotExists(filePath: string, content: string): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Climb the directory tree upward, until it founds a directory that contains the
|
|
26
|
+
* target file, and return resulting full path.
|
|
27
|
+
* Returns `undefined` if the file is not found.
|
|
28
|
+
*/
|
|
29
|
+
export declare function findPath(target: string, startDirectory?: string): string | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* Asynchronously climb the directory tree upward, until it founds a directory that contains the
|
|
32
|
+
* target file, and return resulting full path.
|
|
33
|
+
* Returns `undefined` if the file is not found.
|
|
34
|
+
*/
|
|
35
|
+
export declare function asyncFindPath(target: string, startDirectory?: string): Promise<string | undefined>;
|
package/dist/file.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { access, existsSync } from "node:fs";
|
|
2
|
+
import { constants, mkdir, writeFile } from "node:fs/promises";
|
|
3
|
+
import { dirname, resolve, sep, join } from "path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { execSync, exec, spawnSync } from "node:child_process";
|
|
6
|
+
import { styleText } from "node:util";
|
|
7
|
+
const existsAsync = async (file) => (
|
|
8
|
+
//#endregion existsAsync
|
|
9
|
+
await new Promise((resolve2) => access(file, constants.F_OK, (error) => resolve2(!error)))
|
|
10
|
+
);
|
|
11
|
+
function sh(command, options = {}) {
|
|
12
|
+
try {
|
|
13
|
+
const normalizedOptions = { encoding: "utf8", ...options };
|
|
14
|
+
return execSync(command.trim(), normalizedOptions).trim();
|
|
15
|
+
} catch (error) {
|
|
16
|
+
makeExecErrorReadable(error);
|
|
17
|
+
throw error;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
async function asyncSh(command, options = {}) {
|
|
21
|
+
const normalizedOptions = { encoding: "utf8", ...options };
|
|
22
|
+
return await new Promise((resolve2, reject) => {
|
|
23
|
+
exec(command.trim(), normalizedOptions, (error, stdout, stderr) => {
|
|
24
|
+
if (error) {
|
|
25
|
+
makeExecErrorReadable(error);
|
|
26
|
+
reject(error);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
resolve2(stdout.trim() || stderr.trim());
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
function sp(command, args, options = {}) {
|
|
34
|
+
const normalizedOptions = { encoding: "utf8", ...options };
|
|
35
|
+
const result = spawnSync(command, args, normalizedOptions);
|
|
36
|
+
if (result.error) {
|
|
37
|
+
throw result.error;
|
|
38
|
+
}
|
|
39
|
+
const output = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
|
|
40
|
+
const exitCode = result.status ?? 0;
|
|
41
|
+
if (exitCode !== 0) {
|
|
42
|
+
throw new Error(
|
|
43
|
+
`Command failed with exit code ${String(exitCode)}: ${command} ${args.join(" ")}
|
|
44
|
+
${output}`.trim()
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
return output;
|
|
48
|
+
}
|
|
49
|
+
function makeExecErrorReadable(error) {
|
|
50
|
+
if (error instanceof Error && error.stack && "output" in error && Array.isArray(error.output) && "status" in error) {
|
|
51
|
+
const stackIndex = error.stack.indexOf("\n at ");
|
|
52
|
+
if (stackIndex !== -1) {
|
|
53
|
+
const output = error.output.filter(Boolean).join("\n").trim();
|
|
54
|
+
const newHeader = `${styleText("red", error.message)} (exit code: ${String(error.status)})
|
|
55
|
+
${output}`;
|
|
56
|
+
const oldStackFrames = error.stack.substring(stackIndex);
|
|
57
|
+
error.stack = `Error: ${newHeader}${oldStackFrames}`;
|
|
58
|
+
}
|
|
59
|
+
Object.defineProperties(error, {
|
|
60
|
+
output: { enumerable: false },
|
|
61
|
+
stdout: { enumerable: false },
|
|
62
|
+
stderr: { enumerable: false },
|
|
63
|
+
signal: { enumerable: false },
|
|
64
|
+
status: { enumerable: false },
|
|
65
|
+
pid: { enumerable: false },
|
|
66
|
+
stdio: { enumerable: false }
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async function createFileIfNotExists(filePath, content) {
|
|
71
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
72
|
+
if (!await existsAsync(filePath)) {
|
|
73
|
+
await writeFile(filePath, content, { encoding: "utf8" });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function* getSearchCandidates(target, startDirectory) {
|
|
77
|
+
const resolvedStartDirectory = startDirectory.startsWith("file:///") ? dirname(fileURLToPath(startDirectory)) : resolve(startDirectory);
|
|
78
|
+
const parentPath = resolvedStartDirectory.split(sep);
|
|
79
|
+
while (parentPath.length > searchStopIndex) {
|
|
80
|
+
yield join(
|
|
81
|
+
...sep === "/" ? ["/"] : [],
|
|
82
|
+
...parentPath,
|
|
83
|
+
target
|
|
84
|
+
);
|
|
85
|
+
parentPath.pop();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function findPath(target, startDirectory = process.cwd()) {
|
|
89
|
+
for (const fullPath of getSearchCandidates(target, startDirectory)) {
|
|
90
|
+
if (existsSync(fullPath)) {
|
|
91
|
+
return fullPath;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return void 0;
|
|
95
|
+
}
|
|
96
|
+
const searchStopIndex = 0;
|
|
97
|
+
async function asyncFindPath(target, startDirectory = process.cwd()) {
|
|
98
|
+
for (const fullPath of getSearchCandidates(target, startDirectory)) {
|
|
99
|
+
if (await existsAsync(fullPath)) {
|
|
100
|
+
return fullPath;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return void 0;
|
|
104
|
+
}
|
|
105
|
+
export {
|
|
106
|
+
asyncFindPath,
|
|
107
|
+
asyncSh,
|
|
108
|
+
createFileIfNotExists,
|
|
109
|
+
existsAsync,
|
|
110
|
+
findPath,
|
|
111
|
+
sh,
|
|
112
|
+
sp
|
|
113
|
+
};
|
package/dist/glob.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read `.gitignore` files and convert it to globs that are accepted by ESLint
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts
|
|
6
|
+
* // eslint.config.js
|
|
7
|
+
* import { gitIgnoreFileToGlobs } from "@arcgis/node-toolkit/glob";
|
|
8
|
+
* import { globalIgnores } from "eslint/config";
|
|
9
|
+
*
|
|
10
|
+
* export default [
|
|
11
|
+
* globalIgnores([
|
|
12
|
+
* ...gitIgnoreFileToGlobs(import.meta.dirname + "/.gitignore"),
|
|
13
|
+
* ...gitIgnoreFileToGlobs(import.meta.dirname + "/.prettierignore"),
|
|
14
|
+
* ]),
|
|
15
|
+
* // ...
|
|
16
|
+
* ];
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export declare function gitIgnoreFileToGlobs(filePath: string): string[];
|
|
20
|
+
/**
|
|
21
|
+
* @deprecated Use gitIgnoreFileToGlobs from "@arcgis/node-toolkit/glob"
|
|
22
|
+
* instead
|
|
23
|
+
*/
|
|
24
|
+
export declare const gitIgnoreToGlob: (pattern: string) => string;
|
package/dist/glob.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
function gitIgnoreFileToGlobs(filePath) {
|
|
3
|
+
return readFileSync(filePath, "utf8").split("\n").filter((line) => line.trim().length > 0 && !line.trim().startsWith("#")).map(gitIgnoreToGlob);
|
|
4
|
+
}
|
|
5
|
+
const gitIgnoreToGlob = (pattern) => fixAbsoluteSyntax(fixMatchFilesSyntax(pattern));
|
|
6
|
+
function fixAbsoluteSyntax(pattern) {
|
|
7
|
+
if (pattern.startsWith("/")) {
|
|
8
|
+
return pattern.slice(1);
|
|
9
|
+
}
|
|
10
|
+
if (pattern.startsWith("!/")) {
|
|
11
|
+
return `!${pattern.slice(2)}`;
|
|
12
|
+
}
|
|
13
|
+
const isAlreadyCorrect = pattern.startsWith("**") || pattern.startsWith("!**");
|
|
14
|
+
const basePattern = pattern.startsWith("!") ? pattern.slice(1) : pattern;
|
|
15
|
+
return isAlreadyCorrect ? pattern : `${pattern.startsWith("!") ? "!" : ""}**/${basePattern}`;
|
|
16
|
+
}
|
|
17
|
+
function fixMatchFilesSyntax(pattern) {
|
|
18
|
+
const base = pattern.split("/").at(-1);
|
|
19
|
+
const isAlreadyCorrect = pattern.endsWith("**") || pattern.includes("{") || base?.includes(".");
|
|
20
|
+
if (isAlreadyCorrect) {
|
|
21
|
+
return pattern;
|
|
22
|
+
}
|
|
23
|
+
return pattern.endsWith("/*") ? `${pattern}*` : pattern.endsWith("/") ? `${pattern}**` : `${pattern}/**`;
|
|
24
|
+
}
|
|
25
|
+
export {
|
|
26
|
+
gitIgnoreFileToGlobs,
|
|
27
|
+
gitIgnoreToGlob
|
|
28
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A subset of the package.json typing.
|
|
3
|
+
* Useful for the majority of cases where you just need to read the name, version, and dependencies of a package.json file.
|
|
4
|
+
*
|
|
5
|
+
* The full package.json type is insanely large (20k lines):
|
|
6
|
+
* https://github.com/ffflorian/schemastore-updater/blob/main/schemas/package/index.d.ts#L20067
|
|
7
|
+
*/
|
|
8
|
+
export type PackageJson = {
|
|
9
|
+
"name": string;
|
|
10
|
+
"version": string;
|
|
11
|
+
"private"?: boolean;
|
|
12
|
+
"type"?: "commonjs" | "module";
|
|
13
|
+
"main"?: string;
|
|
14
|
+
"module"?: string;
|
|
15
|
+
"types"?: string;
|
|
16
|
+
"files"?: string[];
|
|
17
|
+
"bin"?: Record<string, string> | string;
|
|
18
|
+
"man"?: string[] | string;
|
|
19
|
+
/**
|
|
20
|
+
* @see https://pnpm.io/package_json#publishconfigdirectory
|
|
21
|
+
*/
|
|
22
|
+
"publishConfig"?: {
|
|
23
|
+
directory?: string;
|
|
24
|
+
linkDirectory?: boolean;
|
|
25
|
+
};
|
|
26
|
+
"dependencies"?: Record<string, string>;
|
|
27
|
+
"devDependencies"?: Record<string, string>;
|
|
28
|
+
"peerDependencies"?: Record<string, string>;
|
|
29
|
+
"peerDependenciesMeta"?: Record<string, {
|
|
30
|
+
optional?: boolean;
|
|
31
|
+
}>;
|
|
32
|
+
"optionalDependencies"?: Record<string, string>;
|
|
33
|
+
"css.customData"?: string[];
|
|
34
|
+
"customElements"?: string;
|
|
35
|
+
"html.customData"?: string[];
|
|
36
|
+
"web-types"?: string;
|
|
37
|
+
"exports"?: Record<string, Record<string, string> | string>;
|
|
38
|
+
"engines"?: {
|
|
39
|
+
node?: string;
|
|
40
|
+
};
|
|
41
|
+
"scripts"?: Record<string, string>;
|
|
42
|
+
"packageManager"?: string;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Synchronously retrieves the package.json file for the current project or a specified location.
|
|
46
|
+
* If the location is not specified, it will search for the package.json file starting from the current working directory.
|
|
47
|
+
* This function caches the package.json file for future calls to avoid unnecessary file system reads.
|
|
48
|
+
*/
|
|
49
|
+
export declare function retrievePackageJson(location?: string): PackageJson;
|
|
50
|
+
/**
|
|
51
|
+
* Asynchronously retrieves the package.json file for the current project or a specified location.
|
|
52
|
+
* If the location is not specified, it will search for the package.json file starting from the current working directory.
|
|
53
|
+
* This function caches the package.json file for future calls to avoid unnecessary file system reads.
|
|
54
|
+
*/
|
|
55
|
+
export declare function asyncRetrievePackageJson(location?: string): Promise<PackageJson>;
|
|
56
|
+
/**
|
|
57
|
+
* Returns an absolute path to the root of a package in node_modules, without
|
|
58
|
+
* trailing slash.
|
|
59
|
+
*/
|
|
60
|
+
export declare function fetchPackageLocation(packageName: string, cwd?: string): Promise<string>;
|
|
61
|
+
/**
|
|
62
|
+
* Detect if current repository/monorepo uses npm, pnpm, or yarn
|
|
63
|
+
*/
|
|
64
|
+
export declare function detectPackageManager(cwd?: string): string;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { path } from "./path.js";
|
|
3
|
+
import { asyncFindPath, findPath } from "./file.js";
|
|
4
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
5
|
+
const cachedPackageJson = {};
|
|
6
|
+
const cachedPackageJsonPromises = {};
|
|
7
|
+
let rootPackageJsonLocation;
|
|
8
|
+
function retrievePackageJson(location) {
|
|
9
|
+
const packageJsonPath = getPackageJsonPath(location);
|
|
10
|
+
cachedPackageJson[packageJsonPath] ??= JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
11
|
+
return cachedPackageJson[packageJsonPath];
|
|
12
|
+
}
|
|
13
|
+
async function asyncRetrievePackageJson(location) {
|
|
14
|
+
const packageJsonPath = getPackageJsonPath(location);
|
|
15
|
+
if (packageJsonPath in cachedPackageJson) {
|
|
16
|
+
return cachedPackageJson[packageJsonPath];
|
|
17
|
+
}
|
|
18
|
+
cachedPackageJsonPromises[packageJsonPath] ??= asyncReadPackageJson(packageJsonPath);
|
|
19
|
+
const result = await cachedPackageJsonPromises[packageJsonPath];
|
|
20
|
+
cachedPackageJson[packageJsonPath] ??= result;
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
23
|
+
const getPackageJsonPath = (location) => location ? path.resolve(location, "package.json") : rootPackageJsonLocation ??= findPath("package.json");
|
|
24
|
+
const asyncReadPackageJson = async (location) => JSON.parse(await readFile(location, "utf-8"));
|
|
25
|
+
const cachedPackageLocation = {};
|
|
26
|
+
const cachedPackageLocationPromises = {};
|
|
27
|
+
async function fetchPackageLocation(packageName, cwd) {
|
|
28
|
+
if (packageName in cachedPackageLocation) {
|
|
29
|
+
return cachedPackageLocation[packageName];
|
|
30
|
+
}
|
|
31
|
+
cachedPackageLocationPromises[packageName] ??= asyncFindPath(
|
|
32
|
+
path.join("node_modules", packageName, "package.json"),
|
|
33
|
+
cwd
|
|
34
|
+
).then((packageJsonLocation) => {
|
|
35
|
+
if (packageJsonLocation === void 0) {
|
|
36
|
+
throw Error(
|
|
37
|
+
`@arcgis/node-toolkit: Unable to resolve package.json location for "${packageName}" package. Current working directory: ${process.cwd()}`
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
return path.dirname(packageJsonLocation);
|
|
41
|
+
});
|
|
42
|
+
const result = await cachedPackageLocationPromises[packageName];
|
|
43
|
+
cachedPackageLocation[packageName] ??= result;
|
|
44
|
+
return cachedPackageLocation[packageName];
|
|
45
|
+
}
|
|
46
|
+
function detectPackageManager(cwd = process.cwd()) {
|
|
47
|
+
let packageManager = void 0;
|
|
48
|
+
{
|
|
49
|
+
const pathParts = path.resolve(cwd).split(path.sep);
|
|
50
|
+
while (pathParts.length > 1) {
|
|
51
|
+
const packageJson = path.join(pathParts.join(path.sep), "package.json");
|
|
52
|
+
pathParts.pop();
|
|
53
|
+
if (!existsSync(packageJson)) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const contents = JSON.parse(readFileSync(packageJson, "utf8"));
|
|
57
|
+
if (typeof contents !== "object" || Array.isArray(contents)) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (typeof contents.packageManager === "string") {
|
|
61
|
+
packageManager ??= contents.packageManager.match(/\w+/u)?.[0] ?? packageManager;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
packageManager ??= "npm";
|
|
66
|
+
return packageManager;
|
|
67
|
+
}
|
|
68
|
+
export {
|
|
69
|
+
asyncRetrievePackageJson,
|
|
70
|
+
detectPackageManager,
|
|
71
|
+
fetchPackageLocation,
|
|
72
|
+
retrievePackageJson
|
|
73
|
+
};
|
package/dist/path.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { posix } from 'path';
|
|
2
|
+
/**
|
|
3
|
+
* Determines if the current environment is POSIX (e.g., macOS, Linux) or not.
|
|
4
|
+
* This is called "isPosix" rather than "isNotWindows" because even if we are
|
|
5
|
+
* on Windows, we could be running in a POSIX environment (e.g. WSL2)
|
|
6
|
+
*/
|
|
7
|
+
export declare const isPosix: boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Converts a Windows-style path to a POSIX-style path.
|
|
10
|
+
*/
|
|
11
|
+
export declare const toPosixPathSeparators: (relativePath: string) => string;
|
|
12
|
+
/**
|
|
13
|
+
* Normalizes the path separators for the current runtime environment.
|
|
14
|
+
* On POSIX systems, this function returns the path unchanged.
|
|
15
|
+
* On Windows systems, it converts all `\` separators to `/`.
|
|
16
|
+
*/
|
|
17
|
+
export declare const normalizePath: (relativePath: string) => string;
|
|
18
|
+
/**
|
|
19
|
+
* Converts a POSIX-style path to a Windows-style path.
|
|
20
|
+
* On Windows, replace all `/` in the path back with `\\`. Do this only if you
|
|
21
|
+
* wish to output the path in the console or error message.
|
|
22
|
+
* On POSIX system (macOS, Linux, ...), this does not change the path (because
|
|
23
|
+
* inside the compiler we use `/` everywhere).
|
|
24
|
+
*/
|
|
25
|
+
export declare const toSystemPathSeparators: (relativePath: string) => string;
|
|
26
|
+
/**
|
|
27
|
+
* Like `process.cwd()`, but always returns a POSIX-style path
|
|
28
|
+
* (with `/` as separator).
|
|
29
|
+
*/
|
|
30
|
+
export declare const getCwd: () => string;
|
|
31
|
+
/**
|
|
32
|
+
* A wrapper for Node.js's `path` module that always uses POSIX-style paths
|
|
33
|
+
* (with `/` as separator).
|
|
34
|
+
*/
|
|
35
|
+
export declare const path: typeof posix & {
|
|
36
|
+
sep: "/";
|
|
37
|
+
};
|
|
38
|
+
export declare const exportsForTests: {
|
|
39
|
+
toWin32PathSeparators: (relativePath: string) => string;
|
|
40
|
+
};
|
package/dist/path.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { sep, posix, win32 } from "path";
|
|
2
|
+
const isPosix = sep === posix.sep;
|
|
3
|
+
const toPosixPathSeparators = (relativePath) => (
|
|
4
|
+
//#endregion toPosixPathSeparators
|
|
5
|
+
relativePath.includes(win32.sep) ? relativePath.replaceAll(win32.sep, posix.sep) : relativePath
|
|
6
|
+
);
|
|
7
|
+
const normalizePath = isPosix ? (path2) => path2 : toPosixPathSeparators;
|
|
8
|
+
const toWin32PathSeparators = (relativePath) => relativePath.includes(posix.sep) ? relativePath.replaceAll(posix.sep, win32.sep) : relativePath;
|
|
9
|
+
const toSystemPathSeparators = isPosix ? (path2) => path2 : toWin32PathSeparators;
|
|
10
|
+
const getCwd = isPosix ? process.cwd : () => toPosixPathSeparators(process.cwd());
|
|
11
|
+
const path = isPosix ? posix : {
|
|
12
|
+
...win32,
|
|
13
|
+
sep: posix.sep,
|
|
14
|
+
join(...paths) {
|
|
15
|
+
const result = win32.join(...paths);
|
|
16
|
+
return toPosixPathSeparators(result);
|
|
17
|
+
},
|
|
18
|
+
normalize(path2) {
|
|
19
|
+
const result = win32.normalize(path2);
|
|
20
|
+
return toPosixPathSeparators(result);
|
|
21
|
+
},
|
|
22
|
+
relative(from, to) {
|
|
23
|
+
const result = win32.relative(from, to);
|
|
24
|
+
return toPosixPathSeparators(result);
|
|
25
|
+
},
|
|
26
|
+
dirname(path2) {
|
|
27
|
+
const result = win32.dirname(path2);
|
|
28
|
+
return toPosixPathSeparators(result);
|
|
29
|
+
},
|
|
30
|
+
resolve(...paths) {
|
|
31
|
+
const result = win32.resolve(...paths);
|
|
32
|
+
return toPosixPathSeparators(result);
|
|
33
|
+
},
|
|
34
|
+
toNamespacedPath(path2) {
|
|
35
|
+
const result = win32.toNamespacedPath(path2);
|
|
36
|
+
return toPosixPathSeparators(result);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
const exportsForTests = { toWin32PathSeparators };
|
|
40
|
+
export {
|
|
41
|
+
exportsForTests,
|
|
42
|
+
getCwd,
|
|
43
|
+
isPosix,
|
|
44
|
+
normalizePath,
|
|
45
|
+
path,
|
|
46
|
+
toPosixPathSeparators,
|
|
47
|
+
toSystemPathSeparators
|
|
48
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { Plugin } from 'vite';
|
|
2
|
+
import { PackageJson } from '../packageJson.ts';
|
|
3
|
+
/**
|
|
4
|
+
* Options for managing dependencies in a Vite project.
|
|
5
|
+
*/
|
|
6
|
+
export type DependencyManagementOptions = {
|
|
7
|
+
/**
|
|
8
|
+
* Force bundle in these dependencies even if they are declared as
|
|
9
|
+
* dependencies or peerDependencies.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* This is desirable if you wish to control the version of a dependency or
|
|
13
|
+
* need to post-process the dependency in some way. Usually, you will declare
|
|
14
|
+
* such as a devDependency, but there is a use case for declaring it as a
|
|
15
|
+
* dependency instead:
|
|
16
|
+
*
|
|
17
|
+
* - If TypeScript types from the bundled in dependencies are referenced in
|
|
18
|
+
* the `.d.ts` files of your library, you will need to declare the package
|
|
19
|
+
* as a `dependency`, so that it is still installed on the consumer's
|
|
20
|
+
* computer so that TypeScript can correctly resolve the types of that
|
|
21
|
+
* library.
|
|
22
|
+
*/
|
|
23
|
+
readonly bundleIn?: (RegExp | string)[];
|
|
24
|
+
/**
|
|
25
|
+
* Force externalize these dependencies, even if they are declared as
|
|
26
|
+
* devDependencies.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* This is desirable if you are sure the end user will have these dependencies
|
|
30
|
+
* available, yet do not wish to declare these as devDependencies for some
|
|
31
|
+
* technical reasons.
|
|
32
|
+
*/
|
|
33
|
+
readonly externalize?: (RegExp | string)[];
|
|
34
|
+
/**
|
|
35
|
+
* By default, this plugin errors if any devDependency is used in runtime code
|
|
36
|
+
* to avoid bundling in dependencies in a library. In application packages,
|
|
37
|
+
* bundling in everything is desirable, so enable this option.
|
|
38
|
+
*
|
|
39
|
+
* @default false
|
|
40
|
+
*/
|
|
41
|
+
readonly isApplication?: boolean;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* By default, Rollup will bundle-in all dependencies.
|
|
45
|
+
*
|
|
46
|
+
* We change it as follows:
|
|
47
|
+
* Externalize all packages that are defined as
|
|
48
|
+
* "dependency" or "peerDependency" in the package.json.
|
|
49
|
+
* If you wish to bundle-in some package, define it as a "devDependency".
|
|
50
|
+
*
|
|
51
|
+
* Bundling-in packages is not recommended because:
|
|
52
|
+
* - it makes our build take longer
|
|
53
|
+
* - it pushes larger packages to NPM
|
|
54
|
+
* - user of our library is locked into the version of the package we bundled in
|
|
55
|
+
* - if user has two packages using the same library, there will be two copies
|
|
56
|
+
* served on the page
|
|
57
|
+
* - It may break some libraries/prevent them from optimizing correctly
|
|
58
|
+
* depending on the production/development mode, or prevent them from loading
|
|
59
|
+
* correct code depending on browser/node.js environment.
|
|
60
|
+
*
|
|
61
|
+
* For example, see this statement from Lit:
|
|
62
|
+
* https://lit.dev/docs/ssr/authoring/#:~:text=Don%27t%20bundle%20Lit,based%20on%20environment.
|
|
63
|
+
*
|
|
64
|
+
* @see {@link ./buildCdn.ts} for CDN dependency bundling details
|
|
65
|
+
*
|
|
66
|
+
* @remarks
|
|
67
|
+
* If a dependency is both a peerDependency and a devDependency, it will still
|
|
68
|
+
* be externalized (because all peerDependencies are externalized).
|
|
69
|
+
*/
|
|
70
|
+
export declare function externalizeDependencies(options: DependencyManagementOptions): Plugin;
|
|
71
|
+
interface PluginShape extends Plugin {
|
|
72
|
+
resolveId: {
|
|
73
|
+
filter: {
|
|
74
|
+
id: {
|
|
75
|
+
include: RegExp[];
|
|
76
|
+
exclude: RegExp[] | undefined;
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
handler: (id: string, importer: string | undefined, options: {
|
|
80
|
+
isEntry: boolean;
|
|
81
|
+
}) => false | undefined;
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
declare function externalizeDependenciesImplementation(options: DependencyManagementOptions, packageJson: PackageJson): PluginShape;
|
|
85
|
+
declare function matchesAny(id: string, patterns: readonly RegExp[]): boolean;
|
|
86
|
+
export declare const exportsForTests: {
|
|
87
|
+
stringToStartsWithGlob: (option: RegExp | string) => RegExp;
|
|
88
|
+
externalizeDependenciesImplementation: typeof externalizeDependenciesImplementation;
|
|
89
|
+
matchesAny: typeof matchesAny;
|
|
90
|
+
};
|
|
91
|
+
export {};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { builtinModules } from "node:module";
|
|
2
|
+
import { styleText } from "node:util";
|
|
3
|
+
import { toPosixPathSeparators } from "../path.js";
|
|
4
|
+
import { retrievePackageJson } from "../packageJson.js";
|
|
5
|
+
function externalizeDependencies(options) {
|
|
6
|
+
return externalizeDependenciesImplementation(options, retrievePackageJson());
|
|
7
|
+
}
|
|
8
|
+
function externalizeDependenciesImplementation(options, packageJson) {
|
|
9
|
+
const externalDependencies = Object.keys({
|
|
10
|
+
...packageJson.dependencies,
|
|
11
|
+
...packageJson.peerDependencies,
|
|
12
|
+
...packageJson.optionalDependencies
|
|
13
|
+
});
|
|
14
|
+
const isStrictBundling = toPosixPathSeparators(import.meta.dirname).includes("support-packages/node-toolkit");
|
|
15
|
+
const bundleIn = options.bundleIn?.map(stringToStartsWithGlob);
|
|
16
|
+
const explicitExternalize = options.externalize?.map(stringToStartsWithGlob) ?? [];
|
|
17
|
+
const externalize = [
|
|
18
|
+
...explicitExternalize,
|
|
19
|
+
// BUG: we shouldn't silently externalize node in browser packages.
|
|
20
|
+
// Consider erroring instead
|
|
21
|
+
/^node:/u,
|
|
22
|
+
new RegExp(
|
|
23
|
+
`^(?:${externalDependencies.join("|")}${externalDependencies.length === 0 ? "" : "|"}${builtinModules.join("|")})(?:/|$)`,
|
|
24
|
+
"u"
|
|
25
|
+
)
|
|
26
|
+
];
|
|
27
|
+
const plugin = {
|
|
28
|
+
name: pluginName,
|
|
29
|
+
apply: "build",
|
|
30
|
+
// Externalize before Vite's default resolution runs
|
|
31
|
+
enforce: "pre",
|
|
32
|
+
// Rolldown also has "external" option, which can be provided regexes.
|
|
33
|
+
// Theoretically that would be more efficient due to less communication
|
|
34
|
+
// overhead, but in practice they always evaluate it on the JS side:
|
|
35
|
+
// https://github.com/rolldown/rolldown/blob/4f996e637732a26ca04972975884abad5183292b/packages/rolldown/src/utils/bindingify-input-options.ts#L167
|
|
36
|
+
// https://github.com/rolldown/rolldown/blob/4f996e637732a26ca04972975884abad5183292b/crates/rolldown_binding/src/utils/normalize_binding_options.rs#L130
|
|
37
|
+
resolveId: {
|
|
38
|
+
filter: {
|
|
39
|
+
id: {
|
|
40
|
+
include: [
|
|
41
|
+
// In most cases, we want all dependencies in library packages to be
|
|
42
|
+
// externalized. Thus, this regex matches all non-relative imports.
|
|
43
|
+
nonRelativeSpecifierPattern,
|
|
44
|
+
// Also include explicitExternalize because those may target relative
|
|
45
|
+
// paths (e.g. ./draconvert.js in mock-services).
|
|
46
|
+
...explicitExternalize
|
|
47
|
+
],
|
|
48
|
+
exclude: bundleIn
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
handler(id, importer, resolveOptions) {
|
|
52
|
+
if (matchesAny(id, externalize)) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
if (
|
|
56
|
+
// Entrypoints look like src/components/button/button.tsx, so are
|
|
57
|
+
// matched by the nonRelativeSpecifierPattern
|
|
58
|
+
resolveOptions.isEntry || // Virtual specifiers are handled by plugins
|
|
59
|
+
id.startsWith("\0") || // data: node: virtual:
|
|
60
|
+
id.includes(":") || // ?raw ?url ?worker - resolved by other plugins
|
|
61
|
+
id.includes("?") || // Applications can bundle in anything
|
|
62
|
+
options.isApplication === true
|
|
63
|
+
) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const error = `[${pluginName}] Vite tried to bundle in "${id}" (imported by ${importer}).
|
|
67
|
+
This is likely undesirable. To externalize it, declare this dependency as a "dependency", "peerDependency" or "optionalDependency" in package.json.
|
|
68
|
+
If this is intentional, add it to the build.dependencies.bundleIn option in useLumina() or bundleIn option in the vitePresetPlugin().
|
|
69
|
+
If this is an application rather than a library, pass isApplication:true in vitePresetPlugin().`;
|
|
70
|
+
if (isStrictBundling) {
|
|
71
|
+
throw Error(error);
|
|
72
|
+
} else {
|
|
73
|
+
console.error(styleText("red", `${error}
|
|
74
|
+
This will be an error in future version.`));
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
return plugin;
|
|
81
|
+
}
|
|
82
|
+
const pluginName = "@arcgis/node-toolkit:externalize-dependencies";
|
|
83
|
+
const stringToStartsWithGlob = (option) => typeof option === "string" ? new RegExp(
|
|
84
|
+
`^${option.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")}${option.endsWith("/") ? "(?:.+)?" : "(?:/.+)?"}$`,
|
|
85
|
+
"u"
|
|
86
|
+
) : option;
|
|
87
|
+
const nonRelativeSpecifierPattern = /^[^\.\/]/u;
|
|
88
|
+
function matchesAny(id, patterns) {
|
|
89
|
+
for (let index = 0; index < patterns.length; ++index) {
|
|
90
|
+
if (patterns[index].test(id)) {
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
const exportsForTests = {
|
|
97
|
+
stringToStartsWithGlob,
|
|
98
|
+
externalizeDependenciesImplementation,
|
|
99
|
+
matchesAny
|
|
100
|
+
};
|
|
101
|
+
export {
|
|
102
|
+
exportsForTests,
|
|
103
|
+
externalizeDependencies
|
|
104
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Plugin } from 'vite';
|
|
2
|
+
import { default as dts } from 'vite-plugin-dts';
|
|
3
|
+
import { DependencyManagementOptions } from './externalizeDependenciesPlugin.ts';
|
|
4
|
+
export type VitePresetOptions = DependencyManagementOptions & {
|
|
5
|
+
/** Options for `vite-plugin-dts` */
|
|
6
|
+
dtsOptions?: Parameters<typeof dts>[0] | false;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Vite preset for all our support packages:
|
|
10
|
+
* - externalizes all non-dev-dependencies
|
|
11
|
+
* - generates type declarations (using `vite-plugin-dts`)
|
|
12
|
+
*/
|
|
13
|
+
export declare function vitePresetPlugin({ dtsOptions, externalize, bundleIn, isApplication }?: VitePresetOptions): [Plugin, Plugin, Promise<Plugin> | undefined];
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { path } from "../path.js";
|
|
2
|
+
import { externalizeDependencies } from "./externalizeDependenciesPlugin.js";
|
|
3
|
+
function shouldSkip(id) {
|
|
4
|
+
return id.includes("__test") || id.includes(".e2e.") || id.includes(".spec.") || id.includes(".test.") || id.includes(".stories.");
|
|
5
|
+
}
|
|
6
|
+
function vitePresetPlugin({ dtsOptions = {}, externalize, bundleIn, isApplication } = {
|
|
7
|
+
externalize: [],
|
|
8
|
+
dtsOptions: {}
|
|
9
|
+
}) {
|
|
10
|
+
const dist = `${path.resolve("dist")}/`;
|
|
11
|
+
const distSrc = `${dist}src/`;
|
|
12
|
+
let command = void 0;
|
|
13
|
+
return [
|
|
14
|
+
{
|
|
15
|
+
name: "vite-preset-config",
|
|
16
|
+
config({ build: { target } = {} }, env) {
|
|
17
|
+
command = env.command;
|
|
18
|
+
return {
|
|
19
|
+
build: {
|
|
20
|
+
// REFACTOR: get this from tsconfig
|
|
21
|
+
// It's a best practice to let the final bundler down-level as needed.
|
|
22
|
+
target: target ?? "es2024"
|
|
23
|
+
},
|
|
24
|
+
define: env.mode === "test" ? {
|
|
25
|
+
"process.env.ESRI_INTERNAL": true
|
|
26
|
+
} : void 0
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
externalizeDependencies({
|
|
31
|
+
externalize,
|
|
32
|
+
bundleIn,
|
|
33
|
+
isApplication
|
|
34
|
+
}),
|
|
35
|
+
// This dependency pulls in many others. Since components-build-utils has a
|
|
36
|
+
// single entry point, we load a large module tree needlessly. To avoid,
|
|
37
|
+
// load the plugin on demand.
|
|
38
|
+
dtsOptions === false ? void 0 : import("vite-plugin-dts").then(
|
|
39
|
+
({ default: dts }) => dts({
|
|
40
|
+
logLevel: "warn",
|
|
41
|
+
...dtsOptions,
|
|
42
|
+
compilerOptions: {
|
|
43
|
+
rootDir: ".",
|
|
44
|
+
...dtsOptions.compilerOptions
|
|
45
|
+
},
|
|
46
|
+
/**
|
|
47
|
+
* Do not emit any .d.ts files for files outside the dist directory
|
|
48
|
+
* (i.e vite.config.ts, storybook stories and etc)
|
|
49
|
+
* This also applies for references to node_modules/.../components.d.ts files in
|
|
50
|
+
* tsconfig.json - these must be included in TypeScript program to provide
|
|
51
|
+
* types, but should not be re-emitted during build.
|
|
52
|
+
*/
|
|
53
|
+
beforeWriteFile: async (filePath, content) => {
|
|
54
|
+
if (filePath.startsWith(distSrc) && !shouldSkip(filePath)) {
|
|
55
|
+
const filePathRelativeToDist = `${dist}${filePath.slice(distSrc.length)}`;
|
|
56
|
+
return dtsOptions?.beforeWriteFile ? await dtsOptions.beforeWriteFile(filePathRelativeToDist, content) : { filePath: filePathRelativeToDist, content };
|
|
57
|
+
} else {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
afterDiagnostic(diagnostics) {
|
|
62
|
+
const hasErrors = diagnostics.length > 0;
|
|
63
|
+
const isBuilding = command === "build";
|
|
64
|
+
const stopBuild = hasErrors && isBuilding;
|
|
65
|
+
if (stopBuild) {
|
|
66
|
+
throw new Error("TypeScript errors reported. See error messages above");
|
|
67
|
+
}
|
|
68
|
+
return dtsOptions?.afterDiagnostic?.(diagnostics);
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
)
|
|
72
|
+
];
|
|
73
|
+
}
|
|
74
|
+
export {
|
|
75
|
+
vitePresetPlugin
|
|
76
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@arcgis/node-toolkit",
|
|
3
|
+
"version": "5.2.0-next.31",
|
|
4
|
+
"description": "Collection of common internal build-time patterns and utilities for ArcGIS Maps SDK for JavaScript components.",
|
|
5
|
+
"homepage": "https://developers.arcgis.com/javascript/latest/",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
"./file": "./dist/file.js",
|
|
9
|
+
"./glob": "./dist/glob.js",
|
|
10
|
+
"./path": "./dist/path.js",
|
|
11
|
+
"./packageJson": "./dist/packageJson.js",
|
|
12
|
+
"./vite/presetPlugin": "./dist/vite/presetPlugin.js",
|
|
13
|
+
"./vite/externalizeDependenciesPlugin": "./dist/vite/externalizeDependenciesPlugin.js",
|
|
14
|
+
"./package.json": "./package.json"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist/"
|
|
18
|
+
],
|
|
19
|
+
"license": "SEE LICENSE IN LICENSE.md",
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@types/node": "~24.11.2",
|
|
22
|
+
"tslib": "^2.8.1",
|
|
23
|
+
"vite": "^7.3.2",
|
|
24
|
+
"vite-plugin-dts": "^4.5.4"
|
|
25
|
+
}
|
|
26
|
+
}
|