@atls/raijin 0.2.0 → 0.2.2

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.
@@ -1,7 +1,7 @@
1
1
  import { RaijinInitializerUsageException } from './exceptions/usage.js';
2
2
  import { installRaijinRuntime } from '../runtime/installer.js';
3
3
  import { runYarnCommand } from '../yarn/command.js';
4
- import { ensurePackageJson } from './project.js';
4
+ import { ensurePackageManifest } from './project.js';
5
5
  import { ensureYarnLock } from './project.js';
6
6
  const CODE_RUNTIME_PACKAGE = '@atls/code-runtime@latest';
7
7
  export const runRaijinInitializer = async ({ argv = [], cwd = process.cwd(), fetchImpl = fetch, runYarnCommand: runCommand = runYarnCommand, } = {}) => {
@@ -9,9 +9,9 @@ export const runRaijinInitializer = async ({ argv = [], cwd = process.cwd(), fet
9
9
  if (argv.length > 1 || (command && command !== 'init')) {
10
10
  throw new RaijinInitializerUsageException();
11
11
  }
12
- await ensurePackageJson(cwd);
12
+ const runtimeManifest = await installRaijinRuntime({ cwd, fetchImpl });
13
+ await ensurePackageManifest(cwd, runtimeManifest.packageManager);
13
14
  await ensureYarnLock(cwd);
14
- await installRaijinRuntime({ cwd, fetchImpl });
15
15
  await runCommand(['add', '-D', CODE_RUNTIME_PACKAGE], cwd);
16
16
  await runCommand(['generate', 'project'], cwd);
17
17
  await runCommand(['tools', 'sync'], cwd);
@@ -1,5 +1,5 @@
1
1
  export declare const getPackageName: (cwd: string) => string;
2
2
  export declare const hasPackageJson: (cwd: string) => Promise<boolean>;
3
3
  export declare const hasYarnLock: (cwd: string) => Promise<boolean>;
4
- export declare const ensurePackageJson: (cwd: string) => Promise<void>;
4
+ export declare const ensurePackageManifest: (cwd: string, packageManager: string) => Promise<void>;
5
5
  export declare const ensureYarnLock: (cwd: string) => Promise<void>;
@@ -1,4 +1,5 @@
1
1
  import { access } from 'node:fs/promises';
2
+ import { readFile } from 'node:fs/promises';
2
3
  import { writeFile } from 'node:fs/promises';
3
4
  import { basename } from 'node:path';
4
5
  import { join } from 'node:path';
@@ -28,11 +29,25 @@ const hasProjectFile = async (cwd, fileName) => {
28
29
  };
29
30
  export const hasPackageJson = async (cwd) => hasProjectFile(cwd, PACKAGE_JSON);
30
31
  export const hasYarnLock = async (cwd) => hasProjectFile(cwd, YARN_LOCK);
31
- export const ensurePackageJson = async (cwd) => {
32
- if (await hasPackageJson(cwd)) {
32
+ const writePackageManifest = async (cwd, manifest) => {
33
+ await writeFile(join(cwd, PACKAGE_JSON), `${JSON.stringify(manifest, null, 2)}\n`);
34
+ };
35
+ export const ensurePackageManifest = async (cwd, packageManager) => {
36
+ if (!(await hasPackageJson(cwd))) {
37
+ await writePackageManifest(cwd, {
38
+ name: getPackageName(cwd),
39
+ packageManager,
40
+ });
41
+ return;
42
+ }
43
+ const manifest = JSON.parse(await readFile(join(cwd, PACKAGE_JSON), 'utf-8'));
44
+ if (manifest.packageManager === packageManager) {
33
45
  return;
34
46
  }
35
- await writeFile(join(cwd, PACKAGE_JSON), `${JSON.stringify({ name: getPackageName(cwd) }, null, 2)}\n`);
47
+ await writePackageManifest(cwd, {
48
+ ...manifest,
49
+ packageManager,
50
+ });
36
51
  };
37
52
  export const ensureYarnLock = async (cwd) => {
38
53
  if (await hasYarnLock(cwd)) {
@@ -11,3 +11,4 @@ export { RAIJIN_RUNTIME_ASSET_NAME } from './manifest.js';
11
11
  export { RAIJIN_RUNTIME_MANIFEST_SCHEMA_VERSION } from './manifest.js';
12
12
  export { RAIJIN_RUNTIME_MANIFEST_URL } from './manifest.js';
13
13
  export { RAIJIN_RUNTIME_PACKAGE_NAME } from './manifest.js';
14
+ export { RAIJIN_RUNTIME_YARN_PATH } from './manifest.js';
@@ -8,3 +8,4 @@ export { RAIJIN_RUNTIME_ASSET_NAME } from "./manifest.js";
8
8
  export { RAIJIN_RUNTIME_MANIFEST_SCHEMA_VERSION } from "./manifest.js";
9
9
  export { RAIJIN_RUNTIME_MANIFEST_URL } from "./manifest.js";
10
10
  export { RAIJIN_RUNTIME_PACKAGE_NAME } from "./manifest.js";
11
+ export { RAIJIN_RUNTIME_YARN_PATH } from "./manifest.js";
@@ -1,4 +1,7 @@
1
+ import { randomUUID } from 'node:crypto';
1
2
  import { mkdir } from 'node:fs/promises';
3
+ import { rename } from 'node:fs/promises';
4
+ import { rm } from 'node:fs/promises';
2
5
  import { writeFile } from 'node:fs/promises';
3
6
  import { dirname } from 'node:path';
4
7
  import { join } from 'node:path';
@@ -8,6 +11,18 @@ import { downloadRaijinRuntime } from './download.js';
8
11
  import { fetchRaijinRuntimeManifest } from './download.js';
9
12
  import { createSha256Digest } from './manifest.js';
10
13
  import { getRaijinRuntimeYarnPath } from './manifest.js';
14
+ const TEMPORARY_RUNTIME_FILE_EXTENSION = '.tmp';
15
+ const writeRuntimeFileAtomically = async (runtimePath, runtime) => {
16
+ const temporaryRuntimePath = `${runtimePath}.${process.pid}.${randomUUID()}${TEMPORARY_RUNTIME_FILE_EXTENSION}`;
17
+ try {
18
+ await writeFile(temporaryRuntimePath, runtime);
19
+ await rename(temporaryRuntimePath, runtimePath);
20
+ }
21
+ catch (error) {
22
+ await rm(temporaryRuntimePath, { force: true });
23
+ throw error;
24
+ }
25
+ };
11
26
  export const installRaijinRuntime = async ({ cwd, fetchImpl, }) => {
12
27
  const manifest = await fetchRaijinRuntimeManifest(fetchImpl);
13
28
  const runtime = await downloadRaijinRuntime(fetchImpl, manifest);
@@ -15,10 +30,10 @@ export const installRaijinRuntime = async ({ cwd, fetchImpl, }) => {
15
30
  if (digest !== manifest.sha256) {
16
31
  throw new RaijinRuntimeDigestMismatchException(manifest.sha256, digest);
17
32
  }
18
- const yarnPath = getRaijinRuntimeYarnPath(manifest);
33
+ const yarnPath = getRaijinRuntimeYarnPath();
19
34
  const runtimePath = join(cwd, yarnPath);
20
35
  await mkdir(dirname(runtimePath), { recursive: true });
21
- await writeFile(runtimePath, runtime);
36
+ await writeRuntimeFileAtomically(runtimePath, runtime);
22
37
  await writeBootstrapConfiguration(cwd, yarnPath);
23
38
  return manifest;
24
39
  };
@@ -2,6 +2,7 @@ export interface RaijinRuntimeManifest {
2
2
  assetName: string;
3
3
  assetUrl: string;
4
4
  packageName: string;
5
+ packageManager: string;
5
6
  schemaVersion: number;
6
7
  sha256: string;
7
8
  tagName: string;
@@ -10,7 +11,8 @@ export interface RaijinRuntimeManifest {
10
11
  export declare const RAIJIN_RUNTIME_MANIFEST_URL = "https://raw.githubusercontent.com/atls/raijin/master/.yarn/releases/raijin-runtime.json";
11
12
  export declare const RAIJIN_RUNTIME_PACKAGE_NAME = "@atls/yarn-cli";
12
13
  export declare const RAIJIN_RUNTIME_ASSET_NAME = "yarn.mjs";
14
+ export declare const RAIJIN_RUNTIME_YARN_PATH = ".yarn/releases/yarn.mjs";
13
15
  export declare const RAIJIN_RUNTIME_MANIFEST_SCHEMA_VERSION = 1;
14
16
  export declare const parseRaijinRuntimeManifest: (value: unknown) => RaijinRuntimeManifest;
15
17
  export declare const createSha256Digest: (data: Buffer) => string;
16
- export declare const getRaijinRuntimeYarnPath: (manifest: RaijinRuntimeManifest) => string;
18
+ export declare const getRaijinRuntimeYarnPath: () => string;
@@ -3,6 +3,7 @@ import { InvalidRaijinRuntimeManifestException } from './exceptions/invalid-mani
3
3
  export const RAIJIN_RUNTIME_MANIFEST_URL = 'https://raw.githubusercontent.com/atls/raijin/master/.yarn/releases/raijin-runtime.json';
4
4
  export const RAIJIN_RUNTIME_PACKAGE_NAME = '@atls/yarn-cli';
5
5
  export const RAIJIN_RUNTIME_ASSET_NAME = 'yarn.mjs';
6
+ export const RAIJIN_RUNTIME_YARN_PATH = '.yarn/releases/yarn.mjs';
6
7
  export const RAIJIN_RUNTIME_MANIFEST_SCHEMA_VERSION = 1;
7
8
  const SHA256_PATTERN = /^[a-f0-9]{64}$/;
8
9
  const isRecord = (value) => typeof value === 'object' && value !== null;
@@ -36,6 +37,7 @@ export const parseRaijinRuntimeManifest = (value) => {
36
37
  assetName,
37
38
  assetUrl: assertManifestString(value, 'assetUrl'),
38
39
  packageName,
40
+ packageManager: assertManifestString(value, 'packageManager'),
39
41
  schemaVersion: RAIJIN_RUNTIME_MANIFEST_SCHEMA_VERSION,
40
42
  sha256,
41
43
  tagName: assertManifestString(value, 'tagName'),
@@ -43,4 +45,4 @@ export const parseRaijinRuntimeManifest = (value) => {
43
45
  };
44
46
  };
45
47
  export const createSha256Digest = (data) => createHash('sha256').update(data).digest('hex');
46
- export const getRaijinRuntimeYarnPath = (manifest) => `.yarn/releases/raijin-yarn-${manifest.version}.mjs`;
48
+ export const getRaijinRuntimeYarnPath = () => RAIJIN_RUNTIME_YARN_PATH;
package/dist/runtime.d.ts CHANGED
@@ -6,3 +6,4 @@ export { RAIJIN_RUNTIME_ASSET_NAME } from './runtime/manifest.js';
6
6
  export { RAIJIN_RUNTIME_MANIFEST_SCHEMA_VERSION } from './runtime/manifest.js';
7
7
  export { RAIJIN_RUNTIME_MANIFEST_URL } from './runtime/manifest.js';
8
8
  export { RAIJIN_RUNTIME_PACKAGE_NAME } from './runtime/manifest.js';
9
+ export { RAIJIN_RUNTIME_YARN_PATH } from './runtime/manifest.js';
package/dist/runtime.js CHANGED
@@ -5,3 +5,4 @@ export { RAIJIN_RUNTIME_ASSET_NAME } from "./runtime/manifest.js";
5
5
  export { RAIJIN_RUNTIME_MANIFEST_SCHEMA_VERSION } from "./runtime/manifest.js";
6
6
  export { RAIJIN_RUNTIME_MANIFEST_URL } from "./runtime/manifest.js";
7
7
  export { RAIJIN_RUNTIME_PACKAGE_NAME } from "./runtime/manifest.js";
8
+ export { RAIJIN_RUNTIME_YARN_PATH } from "./runtime/manifest.js";
@@ -1,3 +1,3 @@
1
1
  import type { YarnCommandRunner } from './runner.js';
2
- export declare const createYarnCommandEnvironment: (environment?: NodeJS.ProcessEnv) => NodeJS.ProcessEnv;
2
+ export declare const createYarnCommandEnvironment: (cwd: string, environment?: NodeJS.ProcessEnv) => NodeJS.ProcessEnv;
3
3
  export declare const runYarnCommand: YarnCommandRunner;
@@ -1,14 +1,121 @@
1
1
  import { spawn } from 'node:child_process';
2
+ import { delimiter } from 'node:path';
2
3
  import { RaijinYarnCommandException } from './exceptions/command.js';
3
- export const createYarnCommandEnvironment = (environment = process.env) => {
4
+ const PATH_ENVIRONMENT_NAME = /^path$/i;
5
+ const TEMPORARY_YARN_BIN_PATH = /[\\/]xfs-[^\\/]*(?:[\\/]|$)/;
6
+ const PNP_NODE_OPTION = /(?:^|[\\/])\.pnp\.(?:cjs|loader\.mjs)$/;
7
+ const NODE_OPTIONS_WITH_VALUE = new Set(['--experimental-loader', '--loader', '--require', '-r']);
8
+ const isPnPNodeOptionValue = (value) => PNP_NODE_OPTION.test(value);
9
+ const splitNodeOptions = (nodeOptions) => {
10
+ const tokens = [];
11
+ let raw = '';
12
+ let value = '';
13
+ let quote;
14
+ for (let index = 0; index < nodeOptions.length; index += 1) {
15
+ const char = nodeOptions[index];
16
+ if (quote) {
17
+ raw += char;
18
+ if (char === '\\' && nodeOptions[index + 1] === quote) {
19
+ index += 1;
20
+ raw += nodeOptions[index];
21
+ value += nodeOptions[index];
22
+ continue;
23
+ }
24
+ if (char === quote) {
25
+ quote = undefined;
26
+ continue;
27
+ }
28
+ value += char;
29
+ continue;
30
+ }
31
+ if (char === '"' || char === "'") {
32
+ raw += char;
33
+ quote = char;
34
+ continue;
35
+ }
36
+ if (/\s/.test(char)) {
37
+ if (raw) {
38
+ tokens.push({ raw, value });
39
+ raw = '';
40
+ value = '';
41
+ }
42
+ continue;
43
+ }
44
+ raw += char;
45
+ value += char;
46
+ }
47
+ if (raw) {
48
+ tokens.push({ raw, value });
49
+ }
50
+ return tokens;
51
+ };
52
+ const removePnPNodeOptions = (nodeOptions) => {
53
+ const options = splitNodeOptions(nodeOptions);
54
+ const filtered = [];
55
+ for (let index = 0; index < options.length; index += 1) {
56
+ const option = options[index];
57
+ const [name, value] = option.value.split('=', 2);
58
+ if (value && NODE_OPTIONS_WITH_VALUE.has(name) && isPnPNodeOptionValue(value)) {
59
+ continue;
60
+ }
61
+ if (NODE_OPTIONS_WITH_VALUE.has(option.value)) {
62
+ const next = options.at(index + 1);
63
+ if (next && isPnPNodeOptionValue(next.value)) {
64
+ index += 1;
65
+ continue;
66
+ }
67
+ }
68
+ filtered.push(option.raw);
69
+ }
70
+ return filtered.join(' ');
71
+ };
72
+ const isTemporaryYarnBinPath = (value) => TEMPORARY_YARN_BIN_PATH.test(value);
73
+ const sanitizePathEnvironment = (value) => value
74
+ .split(delimiter)
75
+ .filter((item) => item && !isTemporaryYarnBinPath(item))
76
+ .join(delimiter);
77
+ const setPathEnvironment = (environment) => {
78
+ const pathName = Object.keys(environment).find((name) => PATH_ENVIRONMENT_NAME.test(name));
79
+ if (!pathName) {
80
+ return;
81
+ }
82
+ const pathValue = environment[pathName];
83
+ if (!pathValue) {
84
+ return;
85
+ }
86
+ const sanitizedPathValue = sanitizePathEnvironment(pathValue);
87
+ if (sanitizedPathValue) {
88
+ environment[pathName] = sanitizedPathValue;
89
+ }
90
+ else {
91
+ Reflect.deleteProperty(environment, pathName);
92
+ }
93
+ };
94
+ export const createYarnCommandEnvironment = (cwd, environment = process.env) => {
4
95
  const yarnEnvironment = { ...environment };
96
+ const nodeOptions = yarnEnvironment.NODE_OPTIONS;
97
+ delete yarnEnvironment.BERRY_BIN_FOLDER;
98
+ delete yarnEnvironment.npm_config_user_agent;
99
+ delete yarnEnvironment.npm_execpath;
5
100
  delete yarnEnvironment.YARN_IGNORE_PATH;
101
+ if (nodeOptions) {
102
+ const sanitizedNodeOptions = removePnPNodeOptions(nodeOptions);
103
+ if (sanitizedNodeOptions) {
104
+ yarnEnvironment.NODE_OPTIONS = sanitizedNodeOptions;
105
+ }
106
+ else {
107
+ delete yarnEnvironment.NODE_OPTIONS;
108
+ }
109
+ }
110
+ setPathEnvironment(yarnEnvironment);
111
+ yarnEnvironment.INIT_CWD = cwd;
112
+ yarnEnvironment.PROJECT_CWD = cwd;
6
113
  return yarnEnvironment;
7
114
  };
8
115
  export const runYarnCommand = async (args, cwd) => {
9
116
  const child = spawn('yarn', args, {
10
117
  cwd,
11
- env: createYarnCommandEnvironment(),
118
+ env: createYarnCommandEnvironment(cwd),
12
119
  shell: process.platform === 'win32',
13
120
  stdio: 'inherit',
14
121
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atls/raijin",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "license": "BSD-3-Clause",
5
5
  "type": "module",
6
6
  "exports": {