feat(core): support compile to wasi target (#22870)
This pull request is trying to add wasm32-wasi target support for the nx/native To test the build, you can run the following commands: - `rustup target add wasm32-wasip1-threads` - `pnpm exec napi build --release --platform --package-json-path packages/nx/package.json --manifest-path packages/nx/Cargo.toml --js ./native-bindings.js -o packages/nx/src/native --target wasm32-wasip1-threads` And the wasm file will be built at packages/nx/src/native/nx.wasm32-wasi.wasm Blocked by: - Support @napi-rs/cli 3.0 Cammisuli/monodon#48 - https://github.com/napi-rs/napi-rs/issues/2009 The pseudo_terminal mod is excluded on the wasm32 targets, which is as expected. The watch mod is excluded because of the upstream `watchexec` deps introduced by ignore-files don't support the wasi target at this moment (but we can improve it). ## Related Issues Fixes https://github.com/nrwl/nx/issues/21860 Fixes https://github.com/nrwl/nx/issues/23821 --------- Co-authored-by: FrozenPandaz <jasonjean1993@gmail.com>
This commit is contained in:
parent
23ce6af2cc
commit
981eb30a0f
13
.github/workflows/publish.yml
vendored
13
.github/workflows/publish.yml
vendored
@ -175,7 +175,7 @@ jobs:
|
||||
sudo apt-get update
|
||||
sudo apt-get install gcc-arm-linux-gnueabihf -y
|
||||
build: |
|
||||
pnpm nx run-many --target=build-native -- --target=armv7-unknown-linux-gnueabihf
|
||||
CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=/usr/bin/arm-linux-gnueabihf-gcc pnpm nx run-many --target=build-native -- --target=armv7-unknown-linux-gnueabihf
|
||||
# Android (not needed)
|
||||
# - host: ubuntu-latest
|
||||
# target: aarch64-linux-android
|
||||
@ -203,7 +203,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ needs.resolve-required-data.outputs.repo }}
|
||||
repository: ${{ needs.resolve-required-data.outputs.repo }}
|
||||
ref: ${{ needs.resolve-required-data.outputs.ref }}
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
@ -281,7 +281,9 @@ jobs:
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: bindings-${{ matrix.settings.target }}
|
||||
path: packages/**/*.node
|
||||
path: |
|
||||
packages/**/*.node
|
||||
packages/**/*.wasm
|
||||
if-no-files-found: error
|
||||
|
||||
build-freebsd:
|
||||
@ -294,7 +296,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
if: ${{ github.event_name != 'schedule' && !github.event.inputs.pr }}
|
||||
with:
|
||||
repository: ${{ needs.resolve-required-data.outputs.repo }}
|
||||
repository: ${{ needs.resolve-required-data.outputs.repo }}
|
||||
ref: ${{ needs.resolve-required-data.outputs.ref }}
|
||||
|
||||
- name: Build
|
||||
@ -396,7 +398,8 @@ jobs:
|
||||
- name: List artifacts
|
||||
run: ls -R artifacts
|
||||
shell: bash
|
||||
|
||||
- name: Build Wasm
|
||||
run: pnpm build:wasm
|
||||
- name: Publish
|
||||
env:
|
||||
VERSION: ${{ needs.resolve-required-data.outputs.version }}
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -56,3 +56,5 @@ out
|
||||
.npm/
|
||||
.profile
|
||||
.rustup/
|
||||
target
|
||||
*.wasm
|
||||
|
||||
@ -17,6 +17,11 @@ packages/nx/src/plugins/js/lock-file/__fixtures__/**/*.*
|
||||
packages/**/schematics/**/files/**/*.html
|
||||
packages/**/generators/**/files/**/*.html
|
||||
packages/nx/src/native/**/*.rs
|
||||
packages/nx/src/native/browser.js
|
||||
packages/nx/src/native/nx.wasi-browser.js
|
||||
packages/nx/src/native/nx.wasi.cjs
|
||||
packages/nx/src/native/wasi-worker-browser.mjs
|
||||
packages/nx/src/native/wasi-worker.mjs
|
||||
packages/nx/src/native/native-bindings.js
|
||||
packages/nx/src/native/index.d.ts
|
||||
nx-dev/nx-dev/.next/
|
||||
|
||||
1105
Cargo.lock
generated
1105
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -135,6 +135,7 @@ It only uses language primitives and immutable objects
|
||||
- [globAsync](../../devkit/documents/globAsync)
|
||||
- [hashArray](../../devkit/documents/hashArray)
|
||||
- [installPackagesTask](../../devkit/documents/installPackagesTask)
|
||||
- [isDaemonEnabled](../../devkit/documents/isDaemonEnabled)
|
||||
- [isWorkspacesEnabled](../../devkit/documents/isWorkspacesEnabled)
|
||||
- [joinPathFragments](../../devkit/documents/joinPathFragments)
|
||||
- [moveFilesToNewDirectory](../../devkit/documents/moveFilesToNewDirectory)
|
||||
|
||||
7
docs/generated/devkit/isDaemonEnabled.md
Normal file
7
docs/generated/devkit/isDaemonEnabled.md
Normal file
@ -0,0 +1,7 @@
|
||||
# Function: isDaemonEnabled
|
||||
|
||||
▸ **isDaemonEnabled**(): `boolean`
|
||||
|
||||
#### Returns
|
||||
|
||||
`boolean`
|
||||
@ -135,6 +135,7 @@ It only uses language primitives and immutable objects
|
||||
- [globAsync](../../devkit/documents/globAsync)
|
||||
- [hashArray](../../devkit/documents/hashArray)
|
||||
- [installPackagesTask](../../devkit/documents/installPackagesTask)
|
||||
- [isDaemonEnabled](../../devkit/documents/isDaemonEnabled)
|
||||
- [isWorkspacesEnabled](../../devkit/documents/isWorkspacesEnabled)
|
||||
- [joinPathFragments](../../devkit/documents/joinPathFragments)
|
||||
- [moveFilesToNewDirectory](../../devkit/documents/moveFilesToNewDirectory)
|
||||
|
||||
@ -60,7 +60,12 @@ describe('js:tsc executor', () => {
|
||||
});
|
||||
const libBuildProcess = await runCommandUntil(
|
||||
`build ${lib} --watch`,
|
||||
(output) => output.includes(`Watching for file changes`)
|
||||
(output) => output.includes(`Watching for file changes`),
|
||||
{
|
||||
env: {
|
||||
NX_DAEMON: 'true',
|
||||
},
|
||||
}
|
||||
);
|
||||
updateFile(`libs/${lib}/README.md`, `Hello, World!`);
|
||||
updateJson(`libs/${lib}/package.json`, (json) => {
|
||||
|
||||
@ -11,6 +11,7 @@ import {
|
||||
uniq,
|
||||
updateFile,
|
||||
updateJson,
|
||||
promisifiedTreeKill,
|
||||
} from '@nx/e2e/utils';
|
||||
import { execSync } from 'child_process';
|
||||
import { join } from 'path';
|
||||
@ -82,6 +83,11 @@ describe('Node Applications + webpack', () => {
|
||||
`serve ${app} --watch --runBuildTargetDependencies`,
|
||||
(output) => {
|
||||
return output.includes(`Hello`);
|
||||
},
|
||||
{
|
||||
env: {
|
||||
NX_DAEMON: 'true',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
@ -106,6 +112,6 @@ describe('Node Applications + webpack', () => {
|
||||
{ timeout: 60_000, ms: 200 }
|
||||
);
|
||||
|
||||
serveProcess.kill();
|
||||
await promisifiedTreeKill(serveProcess.pid, 'SIGKILL');
|
||||
}, 300_000);
|
||||
});
|
||||
|
||||
@ -207,6 +207,11 @@ module.exports = {
|
||||
(output) => {
|
||||
process.stdout.write(output);
|
||||
return output.includes(`foobar: test foo bar`);
|
||||
},
|
||||
{
|
||||
env: {
|
||||
NX_DAEMON: 'true',
|
||||
},
|
||||
}
|
||||
);
|
||||
try {
|
||||
@ -263,8 +268,15 @@ module.exports = {
|
||||
|
||||
// checking serve
|
||||
updateFile(`apps/${nodeapp}/src/assets/file.txt`, `Test`);
|
||||
const p = await runCommandUntil(`serve ${nodeapp}`, (output) =>
|
||||
output.includes(`Listening at http://localhost:${port}`)
|
||||
const p = await runCommandUntil(
|
||||
`serve ${nodeapp}`,
|
||||
(output) => output.includes(`Listening at http://localhost:${port}`),
|
||||
|
||||
{
|
||||
env: {
|
||||
NX_DAEMON: 'true',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
let result = await getData(port);
|
||||
@ -312,6 +324,11 @@ module.exports = {
|
||||
(output) => {
|
||||
process.stdout.write(output);
|
||||
return output.includes(`listening on ws://localhost:${port}`);
|
||||
},
|
||||
{
|
||||
env: {
|
||||
NX_DAEMON: 'true',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
@ -320,7 +337,7 @@ module.exports = {
|
||||
expect(e2eRsult.combinedOutput).toContain('Test Suites: 1 passed, 1 total');
|
||||
|
||||
await killPorts(port);
|
||||
p.kill();
|
||||
await promisifiedTreeKill(p.pid, 'SIGKILL');
|
||||
}, 120000);
|
||||
|
||||
// TODO(crystal, @ndcunningham): how do we handle this now?
|
||||
@ -361,10 +378,18 @@ module.exports = {
|
||||
`
|
||||
);
|
||||
await runCLIAsync(`build ${esmapp}`);
|
||||
const p = await runCommandUntil(`serve ${esmapp}`, (output) => {
|
||||
return output.includes('Hello World');
|
||||
});
|
||||
p.kill();
|
||||
const p = await runCommandUntil(
|
||||
`serve ${esmapp}`,
|
||||
(output) => {
|
||||
return output.includes('Hello World');
|
||||
},
|
||||
{
|
||||
env: {
|
||||
NX_DAEMON: 'true',
|
||||
},
|
||||
}
|
||||
);
|
||||
await promisifiedTreeKill(p.pid, 'SIGKILL');
|
||||
}, 300000);
|
||||
});
|
||||
|
||||
|
||||
11
package.json
11
package.json
@ -21,7 +21,8 @@
|
||||
"echo": "echo 123458",
|
||||
"preinstall": "node ./scripts/preinstall.js",
|
||||
"test": "nx run-many -t test",
|
||||
"e2e": "nx run-many -t e2e --projects ./e2e/*"
|
||||
"e2e": "nx run-many -t e2e --projects ./e2e/*",
|
||||
"build:wasm": "rustup override set nightly-2024-07-04 && rustup target add wasm32-wasip1-threads && pnpm exec nx run-many -t build-native-wasm && rustup override unset"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@actions/core": "^1.10.0",
|
||||
@ -52,8 +53,9 @@
|
||||
"@jest/types": "^29.4.1",
|
||||
"@module-federation/enhanced": "^0.2.3",
|
||||
"@module-federation/sdk": "^0.2.3",
|
||||
"@monodon/rust": "1.3.3",
|
||||
"@napi-rs/cli": "2.14.0",
|
||||
"@monodon/rust": "2.0.0-beta.1",
|
||||
"@napi-rs/cli": "3.0.0-alpha.56",
|
||||
"@napi-rs/wasm-runtime": "0.2.4",
|
||||
"@nestjs/cli": "^10.0.2",
|
||||
"@nestjs/common": "^9.0.0",
|
||||
"@nestjs/core": "^9.0.0",
|
||||
@ -165,6 +167,7 @@
|
||||
"dotenv": "~16.4.5",
|
||||
"dotenv-expand": "~11.0.6",
|
||||
"ejs": "^3.1.7",
|
||||
"emnapi": "^1.2.0",
|
||||
"enhanced-resolve": "^5.8.3",
|
||||
"esbuild": "0.19.5",
|
||||
"eslint": "8.57.0",
|
||||
@ -312,7 +315,7 @@
|
||||
"@heroicons/react": "^2.1.3",
|
||||
"@markdoc/markdoc": "0.2.2",
|
||||
"@monaco-editor/react": "^4.4.6",
|
||||
"@napi-rs/canvas": "^0.1.19",
|
||||
"@napi-rs/canvas": "^0.1.52",
|
||||
"@tailwindcss/aspect-ratio": "^0.4.2",
|
||||
"@tailwindcss/forms": "^0.5.7",
|
||||
"@tailwindcss/typography": "0.5.7",
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
import * as chalk from 'chalk';
|
||||
import { ChildProcess, exec, fork } from 'child_process';
|
||||
import { ChildProcess, fork } from 'child_process';
|
||||
import {
|
||||
ExecutorContext,
|
||||
isDaemonEnabled,
|
||||
joinPathFragments,
|
||||
logger,
|
||||
parseTargetString,
|
||||
ProjectGraphProjectNode,
|
||||
readTargetOptions,
|
||||
runExecutor,
|
||||
Target,
|
||||
} from '@nx/devkit';
|
||||
import { createAsyncIterable } from '@nx/devkit/src/utils/async-iterable';
|
||||
import { daemonClient } from 'nx/src/daemon/client/client';
|
||||
@ -258,23 +258,29 @@ export async function* nodeExecutor(
|
||||
await addToQueue(childProcess, whenReady);
|
||||
await debouncedProcessQueue();
|
||||
};
|
||||
additionalExitHandler = await daemonClient.registerFileWatcher(
|
||||
{
|
||||
watchProjects: [context.projectName],
|
||||
includeDependentProjects: true,
|
||||
},
|
||||
async (err, data) => {
|
||||
if (err === 'closed') {
|
||||
logger.error(`Watch error: Daemon closed the connection`);
|
||||
process.exit(1);
|
||||
} else if (err) {
|
||||
logger.error(`Watch error: ${err?.message ?? 'Unknown'}`);
|
||||
} else {
|
||||
logger.info(`NX File change detected. Restarting...`);
|
||||
await runBuild();
|
||||
if (isDaemonEnabled()) {
|
||||
additionalExitHandler = await daemonClient.registerFileWatcher(
|
||||
{
|
||||
watchProjects: [context.projectName],
|
||||
includeDependentProjects: true,
|
||||
},
|
||||
async (err, data) => {
|
||||
if (err === 'closed') {
|
||||
logger.error(`Watch error: Daemon closed the connection`);
|
||||
process.exit(1);
|
||||
} else if (err) {
|
||||
logger.error(`Watch error: ${err?.message ?? 'Unknown'}`);
|
||||
} else {
|
||||
logger.info(`NX File change detected. Restarting...`);
|
||||
await runBuild();
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
} else {
|
||||
logger.warn(
|
||||
`NX Daemon is not running. Node process will not restart automatically after file changes.`
|
||||
);
|
||||
}
|
||||
await runBuild(); // run first build
|
||||
} else {
|
||||
// Otherwise, run the build executor, which will not run task dependencies.
|
||||
|
||||
@ -1,4 +1,10 @@
|
||||
import { ExecutorContext, TaskGraph, parseTargetString } from '@nx/devkit';
|
||||
import {
|
||||
ExecutorContext,
|
||||
isDaemonEnabled,
|
||||
output,
|
||||
parseTargetString,
|
||||
TaskGraph,
|
||||
} from '@nx/devkit';
|
||||
import { rmSync } from 'fs';
|
||||
import type { BatchExecutorTaskResult } from 'nx/src/config/misc-interfaces';
|
||||
import { getLastValueFromAsyncIterableIterator } from 'nx/src/utils/async-iterator';
|
||||
@ -6,17 +12,17 @@ import { updatePackageJson } from '../../utils/package-json/update-package-json'
|
||||
import type { ExecutorOptions } from '../../utils/schema';
|
||||
import { determineModuleFormatFromTsConfig } from './tsc.impl';
|
||||
import {
|
||||
compileTypescriptSolution,
|
||||
getProcessedTaskTsConfigs,
|
||||
TypescripCompilationLogger,
|
||||
TypescriptCompilationResult,
|
||||
TypescriptInMemoryTsConfig,
|
||||
TypescriptProjectContext,
|
||||
compileTypescriptSolution,
|
||||
getProcessedTaskTsConfigs,
|
||||
} from './lib';
|
||||
import {
|
||||
TaskInfo,
|
||||
createTaskInfoPerTsConfigMap,
|
||||
normalizeTasksOptions,
|
||||
TaskInfo,
|
||||
watchTaskProjectsFileChangesForAssets,
|
||||
watchTaskProjectsPackageJsonFileChanges,
|
||||
} from './lib/batch';
|
||||
@ -115,8 +121,13 @@ export async function* tscBatchExecutor(
|
||||
afterProjectCompilationCallback: processTaskPostCompilation,
|
||||
}
|
||||
);
|
||||
|
||||
if (shouldWatch) {
|
||||
if (shouldWatch && !isDaemonEnabled()) {
|
||||
output.warn({
|
||||
title:
|
||||
'Nx Daemon is not enabled. Assets and package.json files will not be updated on file changes.',
|
||||
});
|
||||
}
|
||||
if (shouldWatch && isDaemonEnabled()) {
|
||||
const taskInfos = Object.values(tsConfigTaskInfoMap);
|
||||
const watchAssetsChangesDisposer =
|
||||
await watchTaskProjectsFileChangesForAssets(taskInfos);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import * as ts from 'typescript';
|
||||
import { ExecutorContext } from '@nx/devkit';
|
||||
import { ExecutorContext, isDaemonEnabled, output } from '@nx/devkit';
|
||||
import type { TypeScriptCompilationOptions } from '@nx/workspace/src/utilities/typescript/compilation';
|
||||
import { CopyAssetsHandler } from '../../utils/assets/copy-assets-handler';
|
||||
import { checkDependencies } from '../../utils/check-dependencies';
|
||||
@ -133,7 +133,14 @@ export async function* tscExecutor(
|
||||
}
|
||||
);
|
||||
|
||||
if (options.watch) {
|
||||
if (!isDaemonEnabled() && options.watch) {
|
||||
output.warn({
|
||||
title:
|
||||
'Nx Daemon is not enabled. Assets and package.json files will not be updated when files change.',
|
||||
});
|
||||
}
|
||||
|
||||
if (isDaemonEnabled() && options.watch) {
|
||||
const disposeWatchAssetChanges =
|
||||
await assetHandler.watchAndProcessOnAssetChange();
|
||||
const disposePackageJsonChanges = await watchForSingleFileChanges(
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { AssetGlob } from './assets';
|
||||
import { CopyAssetsHandler, FileEvent } from './copy-assets-handler';
|
||||
import { ExecutorContext } from '@nx/devkit';
|
||||
import { ExecutorContext, isDaemonEnabled, output } from '@nx/devkit';
|
||||
|
||||
export interface CopyAssetsOptions {
|
||||
outputPath: string;
|
||||
@ -35,7 +35,14 @@ export async function copyAssets(
|
||||
success: true,
|
||||
};
|
||||
|
||||
if (options.watch) {
|
||||
if (!isDaemonEnabled() && options.watch) {
|
||||
output.warn({
|
||||
title:
|
||||
'Nx Daemon is not enabled. Assets will not be updated when they are changed.',
|
||||
});
|
||||
}
|
||||
|
||||
if (isDaemonEnabled() && options.watch) {
|
||||
result.stop = await assetHandler.watchAndProcessOnAssetChange();
|
||||
}
|
||||
|
||||
|
||||
@ -1,2 +1,3 @@
|
||||
native-packages/
|
||||
/*.node
|
||||
**/*.debug.wasm32-wasi.wasm
|
||||
|
||||
@ -3,8 +3,15 @@ name = 'nx'
|
||||
version = '0.1.0'
|
||||
edition = '2021'
|
||||
|
||||
[profile.release-wasi]
|
||||
codegen-units = 16
|
||||
debug = 'full'
|
||||
inherits = "release"
|
||||
lto = "thin"
|
||||
opt-level = "z"
|
||||
strip = "none"
|
||||
|
||||
[dependencies]
|
||||
portable-pty = { git = "https://github.com/cammisuli/wezterm", rev = "b538ee29e1e89eeb4832fb35ae095564dce34c29" }
|
||||
anyhow = "1.0.71"
|
||||
colored = "2"
|
||||
crossbeam-channel = '0.5'
|
||||
@ -12,44 +19,46 @@ dashmap = { version = "5.5.3", features = ["rayon"] }
|
||||
dunce = "1"
|
||||
fs_extra = "1.3.0"
|
||||
globset = "0.4.10"
|
||||
hashbrown = { version = "0.14.3", features = ["rayon", "rkyv"] }
|
||||
hashbrown = { version = "0.14.5", features = ["rayon", "rkyv"] }
|
||||
ignore = '0.4'
|
||||
ignore-files = "2.0.0"
|
||||
itertools = "0.10.5"
|
||||
once_cell = "1.18.0"
|
||||
parking_lot = { version = "0.12.1", features = ["send_guard"] }
|
||||
napi = { version = '2.12.6', default-features = false, features = [
|
||||
napi = { version = '2.16.0', default-features = false, features = [
|
||||
'anyhow',
|
||||
'napi4',
|
||||
'tokio_rt',
|
||||
] }
|
||||
napi-derive = '2.9.3'
|
||||
napi-derive = '2.16.0'
|
||||
nom = '7.1.3'
|
||||
regex = "1.9.1"
|
||||
rayon = "1.7.0"
|
||||
rkyv = { version = "0.7", features = ["validation"] }
|
||||
thiserror = "1.0.40"
|
||||
tokio = { version = "1.28.2", features = ["fs"] }
|
||||
tracing = "0.1.37"
|
||||
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
||||
walkdir = '2.3.3'
|
||||
watchexec = "3.0.1"
|
||||
watchexec-events = "2.0.1"
|
||||
watchexec-filterer-ignore = "3.0.0"
|
||||
watchexec-signals = "2.1.0"
|
||||
xxhash-rust = { version = '0.8.5', features = ['xxh3', 'xxh64'] }
|
||||
swc_common = "0.31.16"
|
||||
swc_ecma_parser = { version = "0.137.1", features = ["typescript"] }
|
||||
swc_ecma_visit = "0.93.0"
|
||||
swc_ecma_ast = "0.107.0"
|
||||
crossterm = "0.27.0"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3", features = ["fileapi"] }
|
||||
|
||||
[target.'cfg(not(windows))'.dependencies]
|
||||
[target.'cfg(all(not(windows), not(target_family = "wasm")))'.dependencies]
|
||||
mio = "0.8"
|
||||
|
||||
[target.'cfg(not(target_family = "wasm"))'.dependencies]
|
||||
portable-pty = { git = "https://github.com/cammisuli/wezterm", rev = "b538ee29e1e89eeb4832fb35ae095564dce34c29" }
|
||||
crossterm = "0.27.0"
|
||||
ignore-files = "2.1.0"
|
||||
watchexec = "3.0.1"
|
||||
watchexec-events = "2.0.1"
|
||||
watchexec-filterer-ignore = "3.0.0"
|
||||
watchexec-signals = "2.1.0"
|
||||
|
||||
[lib]
|
||||
crate-type = ['cdylib']
|
||||
|
||||
|
||||
@ -37,6 +37,7 @@
|
||||
},
|
||||
"homepage": "https://nx.dev",
|
||||
"dependencies": {
|
||||
"@napi-rs/wasm-runtime": "0.2.4",
|
||||
"@yarnpkg/lockfile": "^1.1.0",
|
||||
"@yarnpkg/parsers": "3.0.0-rc.46",
|
||||
"@zkochan/js-yaml": "0.0.7",
|
||||
@ -169,20 +170,23 @@
|
||||
"access": "public"
|
||||
},
|
||||
"napi": {
|
||||
"name": "nx",
|
||||
"package": {
|
||||
"name": "@nx/nx"
|
||||
"binaryName": "nx",
|
||||
"packageName": "@nx/nx",
|
||||
"wasm": {
|
||||
"initialMemory": 16384,
|
||||
"maximumMemory": 32768
|
||||
},
|
||||
"triples": {
|
||||
"additional": [
|
||||
"aarch64-apple-darwin",
|
||||
"aarch64-unknown-linux-gnu",
|
||||
"aarch64-unknown-linux-musl",
|
||||
"aarch64-pc-windows-msvc",
|
||||
"armv7-unknown-linux-gnueabihf",
|
||||
"x86_64-unknown-linux-musl",
|
||||
"x86_64-unknown-freebsd"
|
||||
]
|
||||
}
|
||||
"targets": [
|
||||
"x86_64-unknown-linux-gnu",
|
||||
"x86_64-pc-windows-msvc",
|
||||
"x86_64-apple-darwin",
|
||||
"aarch64-apple-darwin",
|
||||
"aarch64-unknown-linux-gnu",
|
||||
"aarch64-unknown-linux-musl",
|
||||
"aarch64-pc-windows-msvc",
|
||||
"armv7-unknown-linux-gnueabihf",
|
||||
"x86_64-unknown-linux-musl",
|
||||
"x86_64-unknown-freebsd"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,13 +4,39 @@
|
||||
"sourceRoot": "packages/nx",
|
||||
"projectType": "library",
|
||||
"implicitDependencies": ["graph-client"],
|
||||
"release": {
|
||||
"version": {
|
||||
"generator": "@nx/js:release-version"
|
||||
}
|
||||
},
|
||||
"targets": {
|
||||
"build-native-wasm": {
|
||||
"cache": true,
|
||||
"outputs": [
|
||||
"{projectRoot}/src/native/*.wasm",
|
||||
"{projectRoot}/src/native/*.js",
|
||||
"{projectRoot}/src/native/*.cjs",
|
||||
"{projectRoot}/src/native/*.mjs",
|
||||
"{projectRoot}/src/native/index.d.ts"
|
||||
],
|
||||
"executor": "@monodon/rust:napi",
|
||||
"options": {
|
||||
"target": "wasm32-wasip1-threads",
|
||||
"dist": "packages/nx/src/native",
|
||||
"jsFile": "native-bindings.js",
|
||||
"release": true
|
||||
}
|
||||
},
|
||||
"build-native": {
|
||||
"outputs": ["{projectRoot}/src/native/*.node"],
|
||||
"outputs": [
|
||||
"{projectRoot}/src/native/*.node",
|
||||
"{projectRoot}/src/native/*.js",
|
||||
"{projectRoot}/src/native/index.d.ts"
|
||||
],
|
||||
"executor": "@monodon/rust:napi",
|
||||
"options": {
|
||||
"dist": "packages/nx/src/native",
|
||||
"jsFile": "packages/nx/src/native/native-bindings.js",
|
||||
"jsFile": "native-bindings.js",
|
||||
"release": true
|
||||
},
|
||||
"configurations": {
|
||||
@ -24,7 +50,7 @@
|
||||
},
|
||||
"artifacts": {
|
||||
"dependsOn": ["copy-native-package-directories"],
|
||||
"command": "pnpm napi artifacts -c build/packages/nx/package.json -d ./artifacts --dist build/packages"
|
||||
"command": "pnpm napi artifacts --package-json-path build/packages/nx/package.json -d ./artifacts --npm-dir build/packages"
|
||||
},
|
||||
"build-base": {
|
||||
"executor": "@nx/js:tsc",
|
||||
@ -58,7 +84,7 @@
|
||||
},
|
||||
{
|
||||
"input": "packages/nx",
|
||||
"glob": "**/*.{js,css,html,svg}",
|
||||
"glob": "**/*.{mjs,cjs,js,css,html,svg,wasm}",
|
||||
"ignore": ["**/jest.config.js"],
|
||||
"output": "/"
|
||||
},
|
||||
|
||||
@ -540,14 +540,22 @@ async function startServer(
|
||||
environmentJs: string,
|
||||
host: string,
|
||||
port = 4211,
|
||||
watchForchanges = true,
|
||||
watchForChanges = true,
|
||||
affected: string[] = [],
|
||||
focus: string = null,
|
||||
groupByFolder: boolean = false,
|
||||
exclude: string[] = []
|
||||
) {
|
||||
let unregisterFileWatcher: (() => void) | undefined;
|
||||
if (watchForchanges) {
|
||||
|
||||
if (watchForChanges && !daemonClient.enabled()) {
|
||||
output.warn({
|
||||
title:
|
||||
'Nx Daemon is not enabled. Graph will not refresh on file changes.',
|
||||
});
|
||||
}
|
||||
|
||||
if (watchForChanges && daemonClient.enabled()) {
|
||||
unregisterFileWatcher = await createFileWatcher();
|
||||
}
|
||||
|
||||
|
||||
@ -159,6 +159,14 @@ export async function watch(args: WatchArguments) {
|
||||
process.env.NX_VERBOSE_LOGGING = 'true';
|
||||
}
|
||||
|
||||
if (daemonClient.enabled()) {
|
||||
output.error({
|
||||
title:
|
||||
'Daemon is not running. The watch command is not supported without the Nx Daemon.',
|
||||
});
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (
|
||||
args.includeGlobalWorkspaceFiles &&
|
||||
args.command.match(projectReplacementRegex)
|
||||
|
||||
@ -4,7 +4,7 @@ import { readFileSync, statSync } from 'fs';
|
||||
import { FileHandle, open } from 'fs/promises';
|
||||
import { ensureDirSync, ensureFileSync } from 'fs-extra';
|
||||
import { connect } from 'net';
|
||||
import { extname, join } from 'path';
|
||||
import { join } from 'path';
|
||||
import { performance } from 'perf_hooks';
|
||||
import { output } from '../../utils/output';
|
||||
import { getFullOsSocketPath, killSocketOrPath } from '../socket-utils';
|
||||
@ -16,11 +16,10 @@ import {
|
||||
} from '../tmp-dir';
|
||||
import { FileData, ProjectGraph } from '../../config/project-graph';
|
||||
import { isCI } from '../../utils/is-ci';
|
||||
import { NxJsonConfiguration } from '../../config/nx-json';
|
||||
import { hasNxJson, NxJsonConfiguration } from '../../config/nx-json';
|
||||
import { readNxJson } from '../../config/configuration';
|
||||
import { PromisedBasedQueue } from '../../utils/promised-based-queue';
|
||||
import { hasNxJson } from '../../config/nx-json';
|
||||
import { Message, DaemonSocketMessenger } from './daemon-socket-messenger';
|
||||
import { DaemonSocketMessenger, Message } from './daemon-socket-messenger';
|
||||
import { safelyCleanUpExistingProcess } from '../cache';
|
||||
import { Hash } from '../../hasher/task-hasher';
|
||||
import { Task, TaskGraph } from '../../config/task-graph';
|
||||
@ -29,7 +28,7 @@ import {
|
||||
DaemonProjectGraphError,
|
||||
ProjectGraphError,
|
||||
} from '../../project-graph/error-types';
|
||||
import { loadRootEnvFiles } from '../../utils/dotenv';
|
||||
import { IS_WASM, NxWorkspaceFiles } from '../../native';
|
||||
import { HandleGlobMessage } from '../message-types/glob';
|
||||
import {
|
||||
GET_NX_WORKSPACE_FILES,
|
||||
@ -44,7 +43,6 @@ import {
|
||||
HandleGetFilesInDirectoryMessage,
|
||||
} from '../message-types/get-files-in-directory';
|
||||
import { HASH_GLOB, HandleHashGlobMessage } from '../message-types/hash-glob';
|
||||
import { NxWorkspaceFiles } from '../../native';
|
||||
import { TaskRun } from '../../utils/task-history';
|
||||
import {
|
||||
HandleGetTaskHistoryForHashesMessage,
|
||||
@ -70,6 +68,7 @@ enum DaemonStatus {
|
||||
|
||||
export class DaemonClient {
|
||||
private readonly nxJson: NxJsonConfiguration | null;
|
||||
|
||||
constructor() {
|
||||
try {
|
||||
this.nxJson = readNxJson();
|
||||
@ -113,6 +112,7 @@ export class DaemonClient {
|
||||
// CI=true,env=undefined => no daemon
|
||||
// CI=true,env=false => no daemon
|
||||
// CI=true,env=true => daemon
|
||||
// WASM => no daemon because file watching does not work
|
||||
if (
|
||||
(isCI() && env !== 'true') ||
|
||||
isDocker() ||
|
||||
@ -124,6 +124,12 @@ export class DaemonClient {
|
||||
(useDaemonProcessOption === false && env === 'false')
|
||||
) {
|
||||
this._enabled = false;
|
||||
} else if (IS_WASM) {
|
||||
output.warn({
|
||||
title:
|
||||
'The Nx Daemon is unsupported in WebAssembly environments. Some things may be slower than or not function as expected.',
|
||||
});
|
||||
this._enabled = false;
|
||||
} else {
|
||||
this._enabled = true;
|
||||
}
|
||||
@ -550,6 +556,10 @@ export class DaemonClient {
|
||||
|
||||
export const daemonClient = new DaemonClient();
|
||||
|
||||
export function isDaemonEnabled() {
|
||||
return daemonClient.enabled();
|
||||
}
|
||||
|
||||
function isDocker() {
|
||||
try {
|
||||
statSync('/.dockerenv');
|
||||
|
||||
@ -254,3 +254,5 @@ export { cacheDir } from './utils/cache-directory';
|
||||
* @category Utils
|
||||
*/
|
||||
export { createProjectFileMapUsingProjectGraph } from './project-graph/file-map-utils';
|
||||
|
||||
export { isDaemonEnabled } from './daemon/client/client';
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
#![cfg_attr(target_os = "wasi", feature(wasi_ext))]
|
||||
// add all the napi macros globally
|
||||
#[macro_use]
|
||||
extern crate napi_derive;
|
||||
|
||||
1
packages/nx/src/native/browser.js
Normal file
1
packages/nx/src/native/browser.js
Normal file
@ -0,0 +1 @@
|
||||
export * from '@nx/nx-wasm32-wasi'
|
||||
5
packages/nx/src/native/cache/file_ops.rs
vendored
5
packages/nx/src/native/cache/file_ops.rs
vendored
@ -43,6 +43,11 @@ fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) -> io::Result<(
|
||||
std::os::unix::fs::symlink(original, link)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "wasi")]
|
||||
fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) -> io::Result<()> {
|
||||
std::os::wasi::fs::symlink_path(original, link)
|
||||
}
|
||||
|
||||
fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<()> {
|
||||
fs::create_dir_all(&dst)?;
|
||||
for entry in fs::read_dir(src)? {
|
||||
|
||||
@ -22,10 +22,12 @@ pub fn hash_file(file: String) -> Option<String> {
|
||||
#[inline]
|
||||
pub fn hash_file_path<P: AsRef<Path>>(path: P) -> Option<String> {
|
||||
let path = path.as_ref();
|
||||
trace!("Reading {:?} to hash", path);
|
||||
let Ok(content) = std::fs::read(path) else {
|
||||
trace!("Failed to read file: {:?}", path);
|
||||
return None;
|
||||
};
|
||||
trace!("Hashing {:?}", path);
|
||||
let hash = hash(&content);
|
||||
trace!("Hashed file {:?} - {:?}", path, hash);
|
||||
|
||||
|
||||
330
packages/nx/src/native/index.d.ts
vendored
330
packages/nx/src/native/index.d.ts
vendored
@ -1,154 +1,32 @@
|
||||
/* tslint:disable */
|
||||
/* auto-generated by NAPI-RS */
|
||||
/* eslint-disable */
|
||||
|
||||
/* auto-generated by NAPI-RS */
|
||||
|
||||
export class ExternalObject<T> {
|
||||
export declare class ExternalObject<T> {
|
||||
readonly '': {
|
||||
readonly '': unique symbol
|
||||
[K: symbol]: T
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Expands the given entries into a list of existing directories and files.
|
||||
* This is used for copying outputs to and from the cache
|
||||
*/
|
||||
export function expandOutputs(directory: string, entries: Array<string>): Array<string>
|
||||
/**
|
||||
* Expands the given outputs into a list of existing files.
|
||||
* This is used when hashing outputs
|
||||
*/
|
||||
export function getFilesForOutputs(directory: string, entries: Array<string>): Array<string>
|
||||
export function remove(src: string): void
|
||||
export function copy(src: string, dest: string): void
|
||||
export function hashArray(input: Array<string>): string
|
||||
export function hashFile(file: string): string | null
|
||||
export function findImports(projectFileMap: Record<string, Array<string>>): Array<ImportResult>
|
||||
/**
|
||||
* Transfer the project graph from the JS world to the Rust world, so that we can pass the project graph via memory quicker
|
||||
* This wont be needed once the project graph is created in Rust
|
||||
*/
|
||||
export function transferProjectGraph(projectGraph: ProjectGraph): ExternalObject<ProjectGraph>
|
||||
export interface ExternalNode {
|
||||
packageName?: string
|
||||
version: string
|
||||
hash?: string
|
||||
export declare class ChildProcess {
|
||||
kill(): void
|
||||
onExit(callback: (message: string) => void): void
|
||||
onOutput(callback: (message: string) => void): void
|
||||
}
|
||||
export interface Target {
|
||||
executor?: string
|
||||
inputs?: Array<JsInputs>
|
||||
outputs?: Array<string>
|
||||
options?: string
|
||||
configurations?: string
|
||||
|
||||
export declare class HashPlanner {
|
||||
constructor(nxJson: NxJson, projectGraph: ExternalObject<ProjectGraph>)
|
||||
getPlans(taskIds: Array<string>, taskGraph: TaskGraph): Record<string, string[]>
|
||||
getPlansReference(taskIds: Array<string>, taskGraph: TaskGraph): JsExternal
|
||||
}
|
||||
export interface Project {
|
||||
root: string
|
||||
namedInputs?: Record<string, Array<JsInputs>>
|
||||
tags?: Array<string>
|
||||
targets: Record<string, Target>
|
||||
}
|
||||
export interface ProjectGraph {
|
||||
nodes: Record<string, Project>
|
||||
dependencies: Record<string, Array<string>>
|
||||
externalNodes: Record<string, ExternalNode>
|
||||
}
|
||||
export interface HashDetails {
|
||||
value: string
|
||||
details: Record<string, string>
|
||||
}
|
||||
export interface HasherOptions {
|
||||
selectivelyHashTsConfig: boolean
|
||||
}
|
||||
export interface Task {
|
||||
id: string
|
||||
target: TaskTarget
|
||||
outputs: Array<string>
|
||||
projectRoot?: string
|
||||
}
|
||||
export interface TaskTarget {
|
||||
project: string
|
||||
target: string
|
||||
configuration?: string
|
||||
}
|
||||
export interface TaskGraph {
|
||||
roots: Array<string>
|
||||
tasks: Record<string, Task>
|
||||
dependencies: Record<string, Array<string>>
|
||||
}
|
||||
export interface FileData {
|
||||
file: string
|
||||
hash: string
|
||||
}
|
||||
export interface InputsInput {
|
||||
input: string
|
||||
dependencies?: boolean
|
||||
projects?: string | Array<string>
|
||||
}
|
||||
export interface FileSetInput {
|
||||
fileset: string
|
||||
}
|
||||
export interface RuntimeInput {
|
||||
runtime: string
|
||||
}
|
||||
export interface EnvironmentInput {
|
||||
env: string
|
||||
}
|
||||
export interface ExternalDependenciesInput {
|
||||
externalDependencies: Array<string>
|
||||
}
|
||||
export interface DepsOutputsInput {
|
||||
dependentTasksOutputFiles: string
|
||||
transitive?: boolean
|
||||
}
|
||||
/** Stripped version of the NxJson interface for use in rust */
|
||||
export interface NxJson {
|
||||
namedInputs?: Record<string, Array<JsInputs>>
|
||||
}
|
||||
export const enum EventType {
|
||||
delete = 'delete',
|
||||
update = 'update',
|
||||
create = 'create'
|
||||
}
|
||||
export interface WatchEvent {
|
||||
path: string
|
||||
type: EventType
|
||||
}
|
||||
/** Public NAPI error codes that are for Node */
|
||||
export const enum WorkspaceErrors {
|
||||
ParseError = 'ParseError',
|
||||
Generic = 'Generic'
|
||||
}
|
||||
export interface NxWorkspaceFiles {
|
||||
projectFileMap: ProjectFiles
|
||||
globalFiles: Array<FileData>
|
||||
externalReferences?: NxWorkspaceFilesExternals
|
||||
}
|
||||
export interface NxWorkspaceFilesExternals {
|
||||
projectFiles: ExternalObject<ProjectFiles>
|
||||
globalFiles: ExternalObject<Array<FileData>>
|
||||
allWorkspaceFiles: ExternalObject<Array<FileData>>
|
||||
}
|
||||
export interface UpdatedWorkspaceFiles {
|
||||
fileMap: FileMap
|
||||
externalReferences: NxWorkspaceFilesExternals
|
||||
}
|
||||
export interface FileMap {
|
||||
projectFileMap: ProjectFiles
|
||||
nonProjectFiles: Array<FileData>
|
||||
}
|
||||
export function testOnlyTransferFileMap(projectFiles: Record<string, Array<FileData>>, nonProjectFiles: Array<FileData>): NxWorkspaceFilesExternals
|
||||
export class ImportResult {
|
||||
|
||||
export declare class ImportResult {
|
||||
file: string
|
||||
sourceProject: string
|
||||
dynamicImportExpressions: Array<string>
|
||||
staticImportExpressions: Array<string>
|
||||
}
|
||||
export class ChildProcess {
|
||||
kill(): void
|
||||
onExit(callback: (message: string) => void): void
|
||||
onOutput(callback: (message: string) => void): void
|
||||
}
|
||||
export class RustPseudoTerminal {
|
||||
|
||||
export declare class RustPseudoTerminal {
|
||||
constructor()
|
||||
runCommand(command: string, commandDir?: string | undefined | null, jsEnv?: Record<string, string> | undefined | null, execArgv?: Array<string> | undefined | null, quiet?: boolean | undefined | null, tty?: boolean | undefined | null): ChildProcess
|
||||
/**
|
||||
@ -157,16 +35,13 @@ export class RustPseudoTerminal {
|
||||
*/
|
||||
fork(id: string, forkScript: string, pseudoIpcPath: string, commandDir: string | undefined | null, jsEnv: Record<string, string> | undefined | null, execArgv: Array<string> | undefined | null, quiet: boolean): ChildProcess
|
||||
}
|
||||
export class HashPlanner {
|
||||
constructor(nxJson: NxJson, projectGraph: ExternalObject<ProjectGraph>)
|
||||
getPlans(taskIds: Array<string>, taskGraph: TaskGraph): Record<string, string[]>
|
||||
getPlansReference(taskIds: Array<string>, taskGraph: TaskGraph): JsExternal
|
||||
}
|
||||
export class TaskHasher {
|
||||
|
||||
export declare class TaskHasher {
|
||||
constructor(workspaceRoot: string, projectGraph: ExternalObject<ProjectGraph>, projectFileMap: ExternalObject<ProjectFiles>, allWorkspaceFiles: ExternalObject<Array<FileData>>, tsConfig: Buffer, tsConfigPaths: Record<string, Array<string>>, options?: HasherOptions | undefined | null)
|
||||
hashPlans(hashPlans: ExternalObject<Record<string, Array<HashInstruction>>>, jsEnv: Record<string, string>): NapiDashMap
|
||||
}
|
||||
export class Watcher {
|
||||
|
||||
export declare class Watcher {
|
||||
origin: string
|
||||
/**
|
||||
* Creates a new Watcher instance.
|
||||
@ -179,7 +54,8 @@ export class Watcher {
|
||||
watch(callback: (err: string | null, events: WatchEvent[]) => void): void
|
||||
stop(): Promise<void>
|
||||
}
|
||||
export class WorkspaceContext {
|
||||
|
||||
export declare class WorkspaceContext {
|
||||
workspaceRoot: string
|
||||
constructor(workspaceRoot: string, cacheDir: string)
|
||||
getWorkspaceFiles(projectRootMap: Record<string, string>): NxWorkspaceFiles
|
||||
@ -190,3 +66,167 @@ export class WorkspaceContext {
|
||||
allFileData(): Array<FileData>
|
||||
getFilesInDirectory(directory: string): Array<string>
|
||||
}
|
||||
|
||||
export declare export function copy(src: string, dest: string): void
|
||||
|
||||
export interface DepsOutputsInput {
|
||||
dependentTasksOutputFiles: string
|
||||
transitive?: boolean
|
||||
}
|
||||
|
||||
export interface EnvironmentInput {
|
||||
env: string
|
||||
}
|
||||
|
||||
export declare const enum EventType {
|
||||
delete = 'delete',
|
||||
update = 'update',
|
||||
create = 'create'
|
||||
}
|
||||
|
||||
/**
|
||||
* Expands the given entries into a list of existing directories and files.
|
||||
* This is used for copying outputs to and from the cache
|
||||
*/
|
||||
export declare export function expandOutputs(directory: string, entries: Array<string>): Array<string>
|
||||
|
||||
export interface ExternalDependenciesInput {
|
||||
externalDependencies: Array<string>
|
||||
}
|
||||
|
||||
export interface ExternalNode {
|
||||
packageName?: string
|
||||
version: string
|
||||
hash?: string
|
||||
}
|
||||
|
||||
export interface FileData {
|
||||
file: string
|
||||
hash: string
|
||||
}
|
||||
|
||||
export interface FileMap {
|
||||
projectFileMap: ProjectFiles
|
||||
nonProjectFiles: Array<FileData>
|
||||
}
|
||||
|
||||
export interface FileSetInput {
|
||||
fileset: string
|
||||
}
|
||||
|
||||
export declare export function findImports(projectFileMap: Record<string, Array<string>>): Array<ImportResult>
|
||||
|
||||
/**
|
||||
* Expands the given outputs into a list of existing files.
|
||||
* This is used when hashing outputs
|
||||
*/
|
||||
export declare export function getFilesForOutputs(directory: string, entries: Array<string>): Array<string>
|
||||
|
||||
export declare export function hashArray(input: Array<string>): string
|
||||
|
||||
export interface HashDetails {
|
||||
value: string
|
||||
details: Record<string, string>
|
||||
}
|
||||
|
||||
export interface HasherOptions {
|
||||
selectivelyHashTsConfig: boolean
|
||||
}
|
||||
|
||||
export declare export function hashFile(file: string): string | null
|
||||
|
||||
export interface InputsInput {
|
||||
input: string
|
||||
dependencies?: boolean
|
||||
projects?: string | Array<string>
|
||||
}
|
||||
|
||||
export const IS_WASM: boolean
|
||||
|
||||
/** Stripped version of the NxJson interface for use in rust */
|
||||
export interface NxJson {
|
||||
namedInputs?: Record<string, Array<JsInputs>>
|
||||
}
|
||||
|
||||
export interface NxWorkspaceFiles {
|
||||
projectFileMap: ProjectFiles
|
||||
globalFiles: Array<FileData>
|
||||
externalReferences?: NxWorkspaceFilesExternals
|
||||
}
|
||||
|
||||
export interface NxWorkspaceFilesExternals {
|
||||
projectFiles: ExternalObject<ProjectFiles>
|
||||
globalFiles: ExternalObject<Array<FileData>>
|
||||
allWorkspaceFiles: ExternalObject<Array<FileData>>
|
||||
}
|
||||
|
||||
export interface Project {
|
||||
root: string
|
||||
namedInputs?: Record<string, Array<JsInputs>>
|
||||
tags?: Array<string>
|
||||
targets: Record<string, Target>
|
||||
}
|
||||
|
||||
export interface ProjectGraph {
|
||||
nodes: Record<string, Project>
|
||||
dependencies: Record<string, Array<string>>
|
||||
externalNodes: Record<string, ExternalNode>
|
||||
}
|
||||
|
||||
export declare export function remove(src: string): void
|
||||
|
||||
export interface RuntimeInput {
|
||||
runtime: string
|
||||
}
|
||||
|
||||
export interface Target {
|
||||
executor?: string
|
||||
inputs?: Array<JsInputs>
|
||||
outputs?: Array<string>
|
||||
options?: string
|
||||
configurations?: string
|
||||
}
|
||||
|
||||
export interface Task {
|
||||
id: string
|
||||
target: TaskTarget
|
||||
outputs: Array<string>
|
||||
projectRoot?: string
|
||||
}
|
||||
|
||||
export interface TaskGraph {
|
||||
roots: Array<string>
|
||||
tasks: Record<string, Task>
|
||||
dependencies: Record<string, Array<string>>
|
||||
}
|
||||
|
||||
export interface TaskTarget {
|
||||
project: string
|
||||
target: string
|
||||
configuration?: string
|
||||
}
|
||||
|
||||
export declare export function testOnlyTransferFileMap(projectFiles: Record<string, Array<FileData>>, nonProjectFiles: Array<FileData>): NxWorkspaceFilesExternals
|
||||
|
||||
/**
|
||||
* Transfer the project graph from the JS world to the Rust world, so that we can pass the project graph via memory quicker
|
||||
* This wont be needed once the project graph is created in Rust
|
||||
*/
|
||||
export declare export function transferProjectGraph(projectGraph: ProjectGraph): ExternalObject<ProjectGraph>
|
||||
|
||||
export interface UpdatedWorkspaceFiles {
|
||||
fileMap: FileMap
|
||||
externalReferences: NxWorkspaceFilesExternals
|
||||
}
|
||||
|
||||
export interface WatchEvent {
|
||||
path: string
|
||||
type: EventType
|
||||
}
|
||||
|
||||
/** Public NAPI error codes that are for Node */
|
||||
export declare const enum WorkspaceErrors {
|
||||
ParseError = 'ParseError',
|
||||
Generic = 'Generic'
|
||||
}
|
||||
|
||||
|
||||
@ -4,6 +4,22 @@ const Module = require('module');
|
||||
const { nxVersion } = require('../utils/versions');
|
||||
const { getNativeFileCacheLocation } = require('./native-file-cache-location');
|
||||
|
||||
// WASI is still experimental and throws a warning when used
|
||||
// We spawn many many processes so the warning gets printed a lot
|
||||
// We have a different warning elsewhere to warn people using WASI
|
||||
const originalEmit = process.emit;
|
||||
process.emit = function (eventName, eventData) {
|
||||
if (
|
||||
eventName === `warning` &&
|
||||
typeof eventData === `object` &&
|
||||
eventData?.name === `ExperimentalWarning` &&
|
||||
eventData?.message?.includes(`WASI`)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return originalEmit.apply(process, arguments);
|
||||
};
|
||||
|
||||
const nxPackages = new Set([
|
||||
'@nx/nx-android-arm64',
|
||||
'@nx/nx-android-arm-eabi',
|
||||
|
||||
@ -4,10 +4,14 @@ pub mod hasher;
|
||||
mod logger;
|
||||
pub mod plugins;
|
||||
pub mod project_graph;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub mod pseudo_terminal;
|
||||
pub mod tasks;
|
||||
mod types;
|
||||
mod utils;
|
||||
mod walker;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub mod watch;
|
||||
pub mod workspace;
|
||||
|
||||
pub mod wasm;
|
||||
|
||||
@ -1,268 +1,382 @@
|
||||
const { existsSync, readFileSync } = require('fs')
|
||||
const { join } = require('path')
|
||||
// prettier-ignore
|
||||
/* eslint-disable */
|
||||
/* auto-generated by NAPI-RS */
|
||||
|
||||
const { platform, arch } = process
|
||||
const { readFileSync } = require('fs')
|
||||
|
||||
let nativeBinding = null
|
||||
let localFileExisted = false
|
||||
let loadError = null
|
||||
const loadErrors = []
|
||||
|
||||
function isMusl() {
|
||||
// For Node 10
|
||||
if (!process.report || typeof process.report.getReport !== 'function') {
|
||||
try {
|
||||
const lddPath = require('child_process').execSync('which ldd').toString().trim();
|
||||
return readFileSync(lddPath, 'utf8').includes('musl')
|
||||
} catch (e) {
|
||||
return true
|
||||
const isMusl = () => {
|
||||
let musl = false
|
||||
if (process.platform === 'linux') {
|
||||
musl = isMuslFromFilesystem()
|
||||
if (musl === null) {
|
||||
musl = isMuslFromReport()
|
||||
}
|
||||
} else {
|
||||
const { glibcVersionRuntime } = process.report.getReport().header
|
||||
return !glibcVersionRuntime
|
||||
if (musl === null) {
|
||||
musl = isMuslFromChildProcess()
|
||||
}
|
||||
}
|
||||
return musl
|
||||
}
|
||||
|
||||
const isFileMusl = (f) => f.includes('libc.musl-') || f.includes('ld-musl-')
|
||||
|
||||
const isMuslFromFilesystem = () => {
|
||||
try {
|
||||
return readFileSync('/usr/bin/ldd', 'utf-8').includes('musl')
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
switch (platform) {
|
||||
case 'android':
|
||||
switch (arch) {
|
||||
case 'arm64':
|
||||
localFileExisted = existsSync(join(__dirname, 'nx.android-arm64.node'))
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./nx.android-arm64.node')
|
||||
} else {
|
||||
nativeBinding = require('@nx/nx-android-arm64')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
break
|
||||
case 'arm':
|
||||
localFileExisted = existsSync(join(__dirname, 'nx.android-arm-eabi.node'))
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./nx.android-arm-eabi.node')
|
||||
} else {
|
||||
nativeBinding = require('@nx/nx-android-arm-eabi')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
break
|
||||
default:
|
||||
throw new Error(`Unsupported architecture on Android ${arch}`)
|
||||
const isMuslFromReport = () => {
|
||||
const report = typeof process.report.getReport === 'function' ? process.report.getReport() : null
|
||||
if (!report) {
|
||||
return null
|
||||
}
|
||||
if (report.header && report.header.glibcVersionRuntime) {
|
||||
return false
|
||||
}
|
||||
if (Array.isArray(report.sharedObjects)) {
|
||||
if (report.sharedObjects.some(isFileMusl)) {
|
||||
return true
|
||||
}
|
||||
break
|
||||
case 'win32':
|
||||
switch (arch) {
|
||||
case 'x64':
|
||||
localFileExisted = existsSync(
|
||||
join(__dirname, 'nx.win32-x64-msvc.node')
|
||||
)
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./nx.win32-x64-msvc.node')
|
||||
} else {
|
||||
nativeBinding = require('@nx/nx-win32-x64-msvc')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
break
|
||||
case 'ia32':
|
||||
localFileExisted = existsSync(
|
||||
join(__dirname, 'nx.win32-ia32-msvc.node')
|
||||
)
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./nx.win32-ia32-msvc.node')
|
||||
} else {
|
||||
nativeBinding = require('@nx/nx-win32-ia32-msvc')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
break
|
||||
case 'arm64':
|
||||
localFileExisted = existsSync(
|
||||
join(__dirname, 'nx.win32-arm64-msvc.node')
|
||||
)
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./nx.win32-arm64-msvc.node')
|
||||
} else {
|
||||
nativeBinding = require('@nx/nx-win32-arm64-msvc')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
break
|
||||
default:
|
||||
throw new Error(`Unsupported architecture on Windows: ${arch}`)
|
||||
}
|
||||
break
|
||||
case 'darwin':
|
||||
localFileExisted = existsSync(join(__dirname, 'nx.darwin-universal.node'))
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./nx.darwin-universal.node')
|
||||
} else {
|
||||
nativeBinding = require('@nx/nx-darwin-universal')
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const isMuslFromChildProcess = () => {
|
||||
try {
|
||||
return require('child_process').execSync('ldd --version', { encoding: 'utf8' }).includes('musl')
|
||||
} catch (e) {
|
||||
// If we reach this case, we don't know if the system is musl or not, so is better to just fallback to false
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function requireNative() {
|
||||
if (process.platform === 'android') {
|
||||
if (process.arch === 'arm64') {
|
||||
try {
|
||||
return require('./nx.android-arm64.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
break
|
||||
} catch {}
|
||||
switch (arch) {
|
||||
case 'x64':
|
||||
localFileExisted = existsSync(join(__dirname, 'nx.darwin-x64.node'))
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./nx.darwin-x64.node')
|
||||
} else {
|
||||
nativeBinding = require('@nx/nx-darwin-x64')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
break
|
||||
case 'arm64':
|
||||
localFileExisted = existsSync(
|
||||
join(__dirname, 'nx.darwin-arm64.node')
|
||||
)
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./nx.darwin-arm64.node')
|
||||
} else {
|
||||
nativeBinding = require('@nx/nx-darwin-arm64')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
break
|
||||
default:
|
||||
throw new Error(`Unsupported architecture on macOS: ${arch}`)
|
||||
}
|
||||
break
|
||||
case 'freebsd':
|
||||
if (arch !== 'x64') {
|
||||
throw new Error(`Unsupported architecture on FreeBSD: ${arch}`)
|
||||
}
|
||||
localFileExisted = existsSync(join(__dirname, 'nx.freebsd-x64.node'))
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./nx.freebsd-x64.node')
|
||||
} else {
|
||||
nativeBinding = require('@nx/nx-freebsd-x64')
|
||||
try {
|
||||
return require('@nx/nx-android-arm64')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
|
||||
} else if (process.arch === 'arm') {
|
||||
try {
|
||||
return require('./nx.android-arm-eabi.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@nx/nx-android-arm-eabi')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
|
||||
} else {
|
||||
loadErrors.push(new Error(`Unsupported architecture on Android ${process.arch}`))
|
||||
}
|
||||
break
|
||||
case 'linux':
|
||||
switch (arch) {
|
||||
case 'x64':
|
||||
if (isMusl()) {
|
||||
localFileExisted = existsSync(
|
||||
join(__dirname, 'nx.linux-x64-musl.node')
|
||||
)
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./nx.linux-x64-musl.node')
|
||||
} else {
|
||||
nativeBinding = require('@nx/nx-linux-x64-musl')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
} else {
|
||||
localFileExisted = existsSync(
|
||||
join(__dirname, 'nx.linux-x64-gnu.node')
|
||||
)
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./nx.linux-x64-gnu.node')
|
||||
} else {
|
||||
nativeBinding = require('@nx/nx-linux-x64-gnu')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
}
|
||||
break
|
||||
case 'arm64':
|
||||
if (isMusl()) {
|
||||
localFileExisted = existsSync(
|
||||
join(__dirname, 'nx.linux-arm64-musl.node')
|
||||
)
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./nx.linux-arm64-musl.node')
|
||||
} else {
|
||||
nativeBinding = require('@nx/nx-linux-arm64-musl')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
} else {
|
||||
localFileExisted = existsSync(
|
||||
join(__dirname, 'nx.linux-arm64-gnu.node')
|
||||
)
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./nx.linux-arm64-gnu.node')
|
||||
} else {
|
||||
nativeBinding = require('@nx/nx-linux-arm64-gnu')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
}
|
||||
break
|
||||
case 'arm':
|
||||
localFileExisted = existsSync(
|
||||
join(__dirname, 'nx.linux-arm-gnueabihf.node')
|
||||
)
|
||||
} else if (process.platform === 'win32') {
|
||||
if (process.arch === 'x64') {
|
||||
try {
|
||||
return require('./nx.win32-x64-msvc.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@nx/nx-win32-x64-msvc')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
|
||||
} else if (process.arch === 'ia32') {
|
||||
try {
|
||||
return require('./nx.win32-ia32-msvc.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@nx/nx-win32-ia32-msvc')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
|
||||
} else if (process.arch === 'arm64') {
|
||||
try {
|
||||
return require('./nx.win32-arm64-msvc.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@nx/nx-win32-arm64-msvc')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
|
||||
} else {
|
||||
loadErrors.push(new Error(`Unsupported architecture on Windows: ${process.arch}`))
|
||||
}
|
||||
} else if (process.platform === 'darwin') {
|
||||
try {
|
||||
return require('./nx.darwin-universal.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@nx/nx-darwin-universal')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
|
||||
if (process.arch === 'x64') {
|
||||
try {
|
||||
return require('./nx.darwin-x64.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@nx/nx-darwin-x64')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
|
||||
} else if (process.arch === 'arm64') {
|
||||
try {
|
||||
return require('./nx.darwin-arm64.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@nx/nx-darwin-arm64')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
|
||||
} else {
|
||||
loadErrors.push(new Error(`Unsupported architecture on macOS: ${process.arch}`))
|
||||
}
|
||||
} else if (process.platform === 'freebsd') {
|
||||
if (process.arch === 'x64') {
|
||||
try {
|
||||
return require('./nx.freebsd-x64.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@nx/nx-freebsd-x64')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
|
||||
} else if (process.arch === 'arm64') {
|
||||
try {
|
||||
return require('./nx.freebsd-arm64.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@nx/nx-freebsd-arm64')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
|
||||
} else {
|
||||
loadErrors.push(new Error(`Unsupported architecture on FreeBSD: ${process.arch}`))
|
||||
}
|
||||
} else if (process.platform === 'linux') {
|
||||
if (process.arch === 'x64') {
|
||||
if (isMusl()) {
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./nx.linux-arm-gnueabihf.node')
|
||||
} else {
|
||||
nativeBinding = require('@nx/nx-linux-arm-gnueabihf')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
break
|
||||
default:
|
||||
throw new Error(`Unsupported architecture on Linux: ${arch}`)
|
||||
return require('./nx.linux-x64-musl.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@nx/nx-linux-x64-musl')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
|
||||
} else {
|
||||
try {
|
||||
return require('./nx.linux-x64-gnu.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@nx/nx-linux-x64-gnu')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
|
||||
}
|
||||
} else if (process.arch === 'arm64') {
|
||||
if (isMusl()) {
|
||||
try {
|
||||
return require('./nx.linux-arm64-musl.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@nx/nx-linux-arm64-musl')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
|
||||
} else {
|
||||
try {
|
||||
return require('./nx.linux-arm64-gnu.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@nx/nx-linux-arm64-gnu')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
|
||||
}
|
||||
} else if (process.arch === 'arm') {
|
||||
if (isMusl()) {
|
||||
try {
|
||||
return require('./nx.linux-arm-musleabihf.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@nx/nx-linux-arm-musleabihf')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
|
||||
} else {
|
||||
try {
|
||||
return require('./nx.linux-arm-gnueabihf.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@nx/nx-linux-arm-gnueabihf')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
|
||||
}
|
||||
} else if (process.arch === 'riscv64') {
|
||||
if (isMusl()) {
|
||||
try {
|
||||
return require('./nx.linux-riscv64-musl.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@nx/nx-linux-riscv64-musl')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
|
||||
} else {
|
||||
try {
|
||||
return require('./nx.linux-riscv64-gnu.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@nx/nx-linux-riscv64-gnu')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
|
||||
}
|
||||
} else if (process.arch === 'ppc64') {
|
||||
try {
|
||||
return require('./nx.linux-ppc64-gnu.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@nx/nx-linux-ppc64-gnu')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
|
||||
} else if (process.arch === 's390x') {
|
||||
try {
|
||||
return require('./nx.linux-s390x-gnu.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@nx/nx-linux-s390x-gnu')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
|
||||
} else {
|
||||
loadErrors.push(new Error(`Unsupported architecture on Linux: ${process.arch}`))
|
||||
}
|
||||
break
|
||||
default:
|
||||
throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`)
|
||||
} else {
|
||||
loadErrors.push(new Error(`Unsupported OS: ${process.platform}, architecture: ${process.arch}`))
|
||||
}
|
||||
}
|
||||
|
||||
nativeBinding = requireNative()
|
||||
|
||||
if (!nativeBinding || process.env.NAPI_RS_FORCE_WASI) {
|
||||
try {
|
||||
nativeBinding = require('./nx.wasi.cjs')
|
||||
} catch (err) {
|
||||
if (process.env.NAPI_RS_FORCE_WASI) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
if (!nativeBinding) {
|
||||
try {
|
||||
nativeBinding = require('@nx/nx-wasm32-wasi')
|
||||
} catch (err) {
|
||||
if (process.env.NAPI_RS_FORCE_WASI) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!nativeBinding) {
|
||||
if (loadError) {
|
||||
throw loadError
|
||||
if (loadErrors.length > 0) {
|
||||
// TODO Link to documentation with potential fixes
|
||||
// - The package owner could build/publish bindings for this arch
|
||||
// - The user may need to bundle the correct files
|
||||
// - The user may need to re-install node_modules to get new packages
|
||||
throw new Error('Failed to load native binding', { cause: loadErrors })
|
||||
}
|
||||
throw new Error(`Failed to load native binding`)
|
||||
}
|
||||
|
||||
const { expandOutputs, getFilesForOutputs, remove, copy, hashArray, hashFile, ImportResult, findImports, transferProjectGraph, ChildProcess, RustPseudoTerminal, HashPlanner, TaskHasher, EventType, Watcher, WorkspaceContext, WorkspaceErrors, testOnlyTransferFileMap } = nativeBinding
|
||||
|
||||
module.exports.expandOutputs = expandOutputs
|
||||
module.exports.getFilesForOutputs = getFilesForOutputs
|
||||
module.exports.remove = remove
|
||||
module.exports.copy = copy
|
||||
module.exports.hashArray = hashArray
|
||||
module.exports.hashFile = hashFile
|
||||
module.exports.ImportResult = ImportResult
|
||||
module.exports.findImports = findImports
|
||||
module.exports.transferProjectGraph = transferProjectGraph
|
||||
module.exports.ChildProcess = ChildProcess
|
||||
module.exports.RustPseudoTerminal = RustPseudoTerminal
|
||||
module.exports.HashPlanner = HashPlanner
|
||||
module.exports.TaskHasher = TaskHasher
|
||||
module.exports.EventType = EventType
|
||||
module.exports.Watcher = Watcher
|
||||
module.exports.WorkspaceContext = WorkspaceContext
|
||||
module.exports.WorkspaceErrors = WorkspaceErrors
|
||||
module.exports.testOnlyTransferFileMap = testOnlyTransferFileMap
|
||||
module.exports.ChildProcess = nativeBinding.ChildProcess
|
||||
module.exports.HashPlanner = nativeBinding.HashPlanner
|
||||
module.exports.ImportResult = nativeBinding.ImportResult
|
||||
module.exports.RustPseudoTerminal = nativeBinding.RustPseudoTerminal
|
||||
module.exports.TaskHasher = nativeBinding.TaskHasher
|
||||
module.exports.Watcher = nativeBinding.Watcher
|
||||
module.exports.WorkspaceContext = nativeBinding.WorkspaceContext
|
||||
module.exports.copy = nativeBinding.copy
|
||||
module.exports.EventType = nativeBinding.EventType
|
||||
module.exports.expandOutputs = nativeBinding.expandOutputs
|
||||
module.exports.findImports = nativeBinding.findImports
|
||||
module.exports.getFilesForOutputs = nativeBinding.getFilesForOutputs
|
||||
module.exports.hashArray = nativeBinding.hashArray
|
||||
module.exports.hashFile = nativeBinding.hashFile
|
||||
module.exports.IS_WASM = nativeBinding.IS_WASM
|
||||
module.exports.remove = nativeBinding.remove
|
||||
module.exports.testOnlyTransferFileMap = nativeBinding.testOnlyTransferFileMap
|
||||
module.exports.transferProjectGraph = nativeBinding.transferProjectGraph
|
||||
module.exports.WorkspaceErrors = nativeBinding.WorkspaceErrors
|
||||
|
||||
108
packages/nx/src/native/nx.wasi-browser.js
Normal file
108
packages/nx/src/native/nx.wasi-browser.js
Normal file
@ -0,0 +1,108 @@
|
||||
import {
|
||||
instantiateNapiModuleSync as __emnapiInstantiateNapiModuleSync,
|
||||
getDefaultContext as __emnapiGetDefaultContext,
|
||||
WASI as __WASI,
|
||||
createOnMessage as __wasmCreateOnMessageForFsProxy,
|
||||
} from '@napi-rs/wasm-runtime'
|
||||
|
||||
import __wasmUrl from './nx.wasm32-wasi.wasm?url'
|
||||
|
||||
const __wasi = new __WASI({
|
||||
version: 'preview1',
|
||||
})
|
||||
|
||||
const __emnapiContext = __emnapiGetDefaultContext()
|
||||
|
||||
const __sharedMemory = new WebAssembly.Memory({
|
||||
initial: 16384,
|
||||
maximum: 32768,
|
||||
shared: true,
|
||||
})
|
||||
|
||||
const __wasmFile = await fetch(__wasmUrl).then((res) => res.arrayBuffer())
|
||||
|
||||
const {
|
||||
instance: __napiInstance,
|
||||
module: __wasiModule,
|
||||
napiModule: __napiModule,
|
||||
} = __emnapiInstantiateNapiModuleSync(__wasmFile, {
|
||||
context: __emnapiContext,
|
||||
asyncWorkPoolSize: 4,
|
||||
wasi: __wasi,
|
||||
onCreateWorker() {
|
||||
const worker = new Worker(new URL('./wasi-worker-browser.mjs', import.meta.url), {
|
||||
type: 'module',
|
||||
})
|
||||
|
||||
return worker
|
||||
},
|
||||
overwriteImports(importObject) {
|
||||
importObject.env = {
|
||||
...importObject.env,
|
||||
...importObject.napi,
|
||||
...importObject.emnapi,
|
||||
memory: __sharedMemory,
|
||||
}
|
||||
return importObject
|
||||
},
|
||||
beforeInit({ instance }) {
|
||||
__napi_rs_initialize_modules(instance)
|
||||
},
|
||||
})
|
||||
|
||||
function __napi_rs_initialize_modules(__napiInstance) {
|
||||
__napiInstance.exports['__napi_register__expand_outputs_0']?.()
|
||||
__napiInstance.exports['__napi_register__get_files_for_outputs_1']?.()
|
||||
__napiInstance.exports['__napi_register__remove_2']?.()
|
||||
__napiInstance.exports['__napi_register__copy_3']?.()
|
||||
__napiInstance.exports['__napi_register__hash_array_4']?.()
|
||||
__napiInstance.exports['__napi_register__hash_file_5']?.()
|
||||
__napiInstance.exports['__napi_register__ImportResult_struct_6']?.()
|
||||
__napiInstance.exports['__napi_register__find_imports_7']?.()
|
||||
__napiInstance.exports['__napi_register__transfer_project_graph_8']?.()
|
||||
__napiInstance.exports['__napi_register__ExternalNode_struct_9']?.()
|
||||
__napiInstance.exports['__napi_register__Target_struct_10']?.()
|
||||
__napiInstance.exports['__napi_register__Project_struct_11']?.()
|
||||
__napiInstance.exports['__napi_register__ProjectGraph_struct_12']?.()
|
||||
__napiInstance.exports['__napi_register__HashPlanner_struct_13']?.()
|
||||
__napiInstance.exports['__napi_register__HashPlanner_impl_17']?.()
|
||||
__napiInstance.exports['__napi_register__HashDetails_struct_18']?.()
|
||||
__napiInstance.exports['__napi_register__HasherOptions_struct_19']?.()
|
||||
__napiInstance.exports['__napi_register__TaskHasher_struct_20']?.()
|
||||
__napiInstance.exports['__napi_register__TaskHasher_impl_23']?.()
|
||||
__napiInstance.exports['__napi_register__Task_struct_24']?.()
|
||||
__napiInstance.exports['__napi_register__TaskTarget_struct_25']?.()
|
||||
__napiInstance.exports['__napi_register__TaskGraph_struct_26']?.()
|
||||
__napiInstance.exports['__napi_register__FileData_struct_27']?.()
|
||||
__napiInstance.exports['__napi_register__InputsInput_struct_28']?.()
|
||||
__napiInstance.exports['__napi_register__FileSetInput_struct_29']?.()
|
||||
__napiInstance.exports['__napi_register__RuntimeInput_struct_30']?.()
|
||||
__napiInstance.exports['__napi_register__EnvironmentInput_struct_31']?.()
|
||||
__napiInstance.exports['__napi_register__ExternalDependenciesInput_struct_32']?.()
|
||||
__napiInstance.exports['__napi_register__DepsOutputsInput_struct_33']?.()
|
||||
__napiInstance.exports['__napi_register__NxJson_struct_34']?.()
|
||||
__napiInstance.exports['__napi_register__WorkspaceContext_struct_35']?.()
|
||||
__napiInstance.exports['__napi_register__WorkspaceContext_impl_44']?.()
|
||||
__napiInstance.exports['__napi_register__WorkspaceErrors_45']?.()
|
||||
__napiInstance.exports['__napi_register__NxWorkspaceFiles_struct_46']?.()
|
||||
__napiInstance.exports['__napi_register__NxWorkspaceFilesExternals_struct_47']?.()
|
||||
__napiInstance.exports['__napi_register__UpdatedWorkspaceFiles_struct_48']?.()
|
||||
__napiInstance.exports['__napi_register__FileMap_struct_49']?.()
|
||||
__napiInstance.exports['__napi_register____test_only_transfer_file_map_50']?.()
|
||||
__napiInstance.exports['__napi_register__IS_WASM_51']?.()
|
||||
}
|
||||
export const HashPlanner = __napiModule.exports.HashPlanner
|
||||
export const ImportResult = __napiModule.exports.ImportResult
|
||||
export const TaskHasher = __napiModule.exports.TaskHasher
|
||||
export const WorkspaceContext = __napiModule.exports.WorkspaceContext
|
||||
export const copy = __napiModule.exports.copy
|
||||
export const expandOutputs = __napiModule.exports.expandOutputs
|
||||
export const findImports = __napiModule.exports.findImports
|
||||
export const getFilesForOutputs = __napiModule.exports.getFilesForOutputs
|
||||
export const hashArray = __napiModule.exports.hashArray
|
||||
export const hashFile = __napiModule.exports.hashFile
|
||||
export const IS_WASM = __napiModule.exports.IS_WASM
|
||||
export const remove = __napiModule.exports.remove
|
||||
export const testOnlyTransferFileMap = __napiModule.exports.testOnlyTransferFileMap
|
||||
export const transferProjectGraph = __napiModule.exports.transferProjectGraph
|
||||
export const WorkspaceErrors = __napiModule.exports.WorkspaceErrors
|
||||
139
packages/nx/src/native/nx.wasi.cjs
Normal file
139
packages/nx/src/native/nx.wasi.cjs
Normal file
@ -0,0 +1,139 @@
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
|
||||
/* auto-generated by NAPI-RS */
|
||||
|
||||
const __nodeFs = require('node:fs')
|
||||
const __nodePath = require('node:path')
|
||||
const { WASI: __nodeWASI } = require('node:wasi')
|
||||
const { Worker } = require('node:worker_threads')
|
||||
|
||||
const {
|
||||
instantiateNapiModuleSync: __emnapiInstantiateNapiModuleSync,
|
||||
getDefaultContext: __emnapiGetDefaultContext,
|
||||
createOnMessage: __wasmCreateOnMessageForFsProxy,
|
||||
} = require('@napi-rs/wasm-runtime')
|
||||
|
||||
const __rootDir = __nodePath.parse(process.cwd()).root
|
||||
|
||||
const __wasi = new __nodeWASI({
|
||||
version: 'preview1',
|
||||
env: process.env,
|
||||
preopens: {
|
||||
[__rootDir]: __rootDir,
|
||||
}
|
||||
})
|
||||
|
||||
const __emnapiContext = __emnapiGetDefaultContext()
|
||||
|
||||
const __sharedMemory = new WebAssembly.Memory({
|
||||
initial: 16384,
|
||||
maximum: 32768,
|
||||
shared: true,
|
||||
})
|
||||
|
||||
let __wasmFilePath = __nodePath.join(__dirname, 'nx.wasm32-wasi.wasm')
|
||||
const __wasmDebugFilePath = __nodePath.join(__dirname, 'nx.wasm32-wasi.debug.wasm')
|
||||
|
||||
if (__nodeFs.existsSync(__wasmDebugFilePath)) {
|
||||
__wasmFilePath = __wasmDebugFilePath
|
||||
} else if (!__nodeFs.existsSync(__wasmFilePath)) {
|
||||
try {
|
||||
__wasmFilePath = __nodePath.resolve('@nx/nx-wasm32-wasi')
|
||||
} catch {
|
||||
throw new Error('Cannot find nx.wasm32-wasi.wasm file, and @nx/nx-wasm32-wasi package is not installed.')
|
||||
}
|
||||
}
|
||||
|
||||
const { instance: __napiInstance, module: __wasiModule, napiModule: __napiModule } = __emnapiInstantiateNapiModuleSync(__nodeFs.readFileSync(__wasmFilePath), {
|
||||
context: __emnapiContext,
|
||||
asyncWorkPoolSize: (function() {
|
||||
const threadsSizeFromEnv = Number(process.env.NAPI_RS_ASYNC_WORK_POOL_SIZE ?? process.env.UV_THREADPOOL_SIZE)
|
||||
// NaN > 0 is false
|
||||
if (threadsSizeFromEnv > 0) {
|
||||
return threadsSizeFromEnv
|
||||
} else {
|
||||
return 4
|
||||
}
|
||||
})(),
|
||||
wasi: __wasi,
|
||||
onCreateWorker() {
|
||||
const worker = new Worker(__nodePath.join(__dirname, 'wasi-worker.mjs'), {
|
||||
env: process.env,
|
||||
execArgv: ['--experimental-wasi-unstable-preview1'],
|
||||
})
|
||||
worker.onmessage = ({ data }) => {
|
||||
__wasmCreateOnMessageForFsProxy(__nodeFs)(data)
|
||||
}
|
||||
return worker
|
||||
},
|
||||
overwriteImports(importObject) {
|
||||
importObject.env = {
|
||||
...importObject.env,
|
||||
...importObject.napi,
|
||||
...importObject.emnapi,
|
||||
memory: __sharedMemory,
|
||||
}
|
||||
return importObject
|
||||
},
|
||||
beforeInit({ instance }) {
|
||||
__napi_rs_initialize_modules(instance)
|
||||
}
|
||||
})
|
||||
|
||||
function __napi_rs_initialize_modules(__napiInstance) {
|
||||
__napiInstance.exports['__napi_register__expand_outputs_0']?.()
|
||||
__napiInstance.exports['__napi_register__get_files_for_outputs_1']?.()
|
||||
__napiInstance.exports['__napi_register__remove_2']?.()
|
||||
__napiInstance.exports['__napi_register__copy_3']?.()
|
||||
__napiInstance.exports['__napi_register__hash_array_4']?.()
|
||||
__napiInstance.exports['__napi_register__hash_file_5']?.()
|
||||
__napiInstance.exports['__napi_register__ImportResult_struct_6']?.()
|
||||
__napiInstance.exports['__napi_register__find_imports_7']?.()
|
||||
__napiInstance.exports['__napi_register__transfer_project_graph_8']?.()
|
||||
__napiInstance.exports['__napi_register__ExternalNode_struct_9']?.()
|
||||
__napiInstance.exports['__napi_register__Target_struct_10']?.()
|
||||
__napiInstance.exports['__napi_register__Project_struct_11']?.()
|
||||
__napiInstance.exports['__napi_register__ProjectGraph_struct_12']?.()
|
||||
__napiInstance.exports['__napi_register__HashPlanner_struct_13']?.()
|
||||
__napiInstance.exports['__napi_register__HashPlanner_impl_17']?.()
|
||||
__napiInstance.exports['__napi_register__HashDetails_struct_18']?.()
|
||||
__napiInstance.exports['__napi_register__HasherOptions_struct_19']?.()
|
||||
__napiInstance.exports['__napi_register__TaskHasher_struct_20']?.()
|
||||
__napiInstance.exports['__napi_register__TaskHasher_impl_23']?.()
|
||||
__napiInstance.exports['__napi_register__Task_struct_24']?.()
|
||||
__napiInstance.exports['__napi_register__TaskTarget_struct_25']?.()
|
||||
__napiInstance.exports['__napi_register__TaskGraph_struct_26']?.()
|
||||
__napiInstance.exports['__napi_register__FileData_struct_27']?.()
|
||||
__napiInstance.exports['__napi_register__InputsInput_struct_28']?.()
|
||||
__napiInstance.exports['__napi_register__FileSetInput_struct_29']?.()
|
||||
__napiInstance.exports['__napi_register__RuntimeInput_struct_30']?.()
|
||||
__napiInstance.exports['__napi_register__EnvironmentInput_struct_31']?.()
|
||||
__napiInstance.exports['__napi_register__ExternalDependenciesInput_struct_32']?.()
|
||||
__napiInstance.exports['__napi_register__DepsOutputsInput_struct_33']?.()
|
||||
__napiInstance.exports['__napi_register__NxJson_struct_34']?.()
|
||||
__napiInstance.exports['__napi_register__WorkspaceContext_struct_35']?.()
|
||||
__napiInstance.exports['__napi_register__WorkspaceContext_impl_44']?.()
|
||||
__napiInstance.exports['__napi_register__WorkspaceErrors_45']?.()
|
||||
__napiInstance.exports['__napi_register__NxWorkspaceFiles_struct_46']?.()
|
||||
__napiInstance.exports['__napi_register__NxWorkspaceFilesExternals_struct_47']?.()
|
||||
__napiInstance.exports['__napi_register__UpdatedWorkspaceFiles_struct_48']?.()
|
||||
__napiInstance.exports['__napi_register__FileMap_struct_49']?.()
|
||||
__napiInstance.exports['__napi_register____test_only_transfer_file_map_50']?.()
|
||||
__napiInstance.exports['__napi_register__IS_WASM_51']?.()
|
||||
}
|
||||
module.exports.HashPlanner = __napiModule.exports.HashPlanner
|
||||
module.exports.ImportResult = __napiModule.exports.ImportResult
|
||||
module.exports.TaskHasher = __napiModule.exports.TaskHasher
|
||||
module.exports.WorkspaceContext = __napiModule.exports.WorkspaceContext
|
||||
module.exports.copy = __napiModule.exports.copy
|
||||
module.exports.expandOutputs = __napiModule.exports.expandOutputs
|
||||
module.exports.findImports = __napiModule.exports.findImports
|
||||
module.exports.getFilesForOutputs = __napiModule.exports.getFilesForOutputs
|
||||
module.exports.hashArray = __napiModule.exports.hashArray
|
||||
module.exports.hashFile = __napiModule.exports.hashFile
|
||||
module.exports.IS_WASM = __napiModule.exports.IS_WASM
|
||||
module.exports.remove = __napiModule.exports.remove
|
||||
module.exports.testOnlyTransferFileMap = __napiModule.exports.testOnlyTransferFileMap
|
||||
module.exports.transferProjectGraph = __napiModule.exports.transferProjectGraph
|
||||
module.exports.WorkspaceErrors = __napiModule.exports.WorkspaceErrors
|
||||
34
packages/nx/src/native/utils/atomics/default.rs
Normal file
34
packages/nx/src/native/utils/atomics/default.rs
Normal file
@ -0,0 +1,34 @@
|
||||
use parking_lot::{Condvar, Mutex, MutexGuard};
|
||||
|
||||
pub struct NxMutex<T>(Mutex<T>);
|
||||
|
||||
impl <T> NxMutex<T> {
|
||||
pub fn new(value: T) -> Self {
|
||||
Self(Mutex::new(value))
|
||||
}
|
||||
pub fn lock(&self) -> anyhow::Result<MutexGuard<'_, T>> {
|
||||
Ok(self.0.lock())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NxCondvar(Condvar);
|
||||
|
||||
impl NxCondvar {
|
||||
pub fn new() -> Self {
|
||||
Self(Condvar::new())
|
||||
}
|
||||
|
||||
pub fn wait<'a, T, F>(&'a self, mut guard: MutexGuard<'a, T>, condition: F) -> anyhow::Result<MutexGuard<'a, T>>
|
||||
where
|
||||
F: Fn(&MutexGuard<'a, T>) -> bool
|
||||
{
|
||||
if condition(&guard) {
|
||||
self.0.wait(&mut guard);
|
||||
}
|
||||
Ok(guard)
|
||||
}
|
||||
|
||||
pub fn notify_all(&self) {
|
||||
self.0.notify_all();
|
||||
}
|
||||
}
|
||||
27
packages/nx/src/native/utils/atomics/wasm.rs
Normal file
27
packages/nx/src/native/utils/atomics/wasm.rs
Normal file
@ -0,0 +1,27 @@
|
||||
use std::sync::{Condvar, LockResult, Mutex, MutexGuard};
|
||||
|
||||
pub struct NxMutex<T>(Mutex<T>);
|
||||
|
||||
impl <T> NxMutex<T> {
|
||||
pub fn new(value: T) -> Self {
|
||||
Self(Mutex::new(value))
|
||||
}
|
||||
pub fn lock(&self) -> LockResult<MutexGuard<'_, T>> {
|
||||
self.0.lock()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NxCondvar(Condvar);
|
||||
|
||||
impl NxCondvar {
|
||||
pub fn new() -> Self {
|
||||
Self(Condvar::new())
|
||||
}
|
||||
|
||||
pub fn wait<'a, T, F>(&self, mutex_guard: MutexGuard<'a, T>, condition: F) -> LockResult<MutexGuard<'a, T>>
|
||||
where
|
||||
F: Fn(&mut T) -> bool
|
||||
{
|
||||
self.0.wait_while(mutex_guard, condition)
|
||||
}
|
||||
}
|
||||
@ -17,3 +17,9 @@ pub fn get_mod_time(metadata: &Metadata) -> i64 {
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
metadata.mtime()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "wasi")]
|
||||
pub fn get_mod_time(metadata: &Metadata) -> i64 {
|
||||
use std::os::wasi::fs::MetadataExt;
|
||||
metadata.mtim() as i64
|
||||
}
|
||||
|
||||
@ -6,3 +6,10 @@ pub mod path;
|
||||
pub use find_matching_projects::*;
|
||||
pub use get_mod_time::*;
|
||||
pub use normalize_trait::Normalize;
|
||||
|
||||
|
||||
#[cfg_attr(not(target_arch = "wasm32"), path = "atomics/default.rs")]
|
||||
#[cfg_attr(target_arch = "wasm32", path = "atomics/wasm.rs")]
|
||||
pub mod atomics;
|
||||
|
||||
pub use atomics::*;
|
||||
|
||||
@ -1,10 +1,6 @@
|
||||
use std::fmt::Debug;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::thread;
|
||||
use std::thread::available_parallelism;
|
||||
|
||||
use crossbeam_channel::unbounded;
|
||||
use ignore::WalkBuilder;
|
||||
use tracing::trace;
|
||||
use ignore::{WalkBuilder};
|
||||
|
||||
use crate::native::glob::build_glob_set;
|
||||
|
||||
@ -60,30 +56,55 @@ where
|
||||
}
|
||||
|
||||
/// Walk the directory and ignore files from .gitignore and .nxignore
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub fn nx_walker<P>(directory: P) -> impl Iterator<Item = NxFile>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let directory: PathBuf = directory.as_ref().into();
|
||||
let walker = create_walker(&directory);
|
||||
|
||||
let entries = walker.build();
|
||||
|
||||
entries.filter_map(move |entry| {
|
||||
let Ok(dir_entry) = entry else {
|
||||
return None;
|
||||
};
|
||||
|
||||
if dir_entry.file_type().is_some_and(|d| d.is_dir()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let Ok(file_path) = dir_entry.path().strip_prefix(&directory) else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let Ok(metadata) = dir_entry.metadata() else {
|
||||
return None;
|
||||
};
|
||||
|
||||
Some(NxFile {
|
||||
full_path: String::from(dir_entry.path().to_string_lossy()),
|
||||
normalized_path: file_path.to_normalized_string(),
|
||||
mod_time: get_mod_time(&metadata),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Walk the directory and ignore files from .gitignore and .nxignore
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn nx_walker<P>(directory: P) -> impl Iterator<Item = NxFile>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
use std::thread;
|
||||
use std::thread::available_parallelism;
|
||||
|
||||
use crossbeam_channel::unbounded;
|
||||
use tracing::trace;
|
||||
|
||||
let directory = directory.as_ref();
|
||||
|
||||
let ignore_glob_set = build_glob_set(&[
|
||||
"**/node_modules",
|
||||
"**/.git",
|
||||
"**/.nx/cache",
|
||||
"**/.nx/workspace-data",
|
||||
"**/.yarn/cache",
|
||||
])
|
||||
.expect("These static ignores always build");
|
||||
|
||||
let mut walker = WalkBuilder::new(directory);
|
||||
walker.hidden(false);
|
||||
walker.add_custom_ignore_filename(".nxignore");
|
||||
|
||||
// We should make sure to always ignore node_modules and the .git folder
|
||||
walker.filter_entry(move |entry| {
|
||||
let path = entry.path().to_string_lossy();
|
||||
!ignore_glob_set.is_match(path.as_ref())
|
||||
});
|
||||
let mut walker = create_walker(directory);
|
||||
|
||||
let cpus = available_parallelism().map_or(2, |n| n.get()) - 1;
|
||||
|
||||
@ -118,7 +139,7 @@ where
|
||||
normalized_path: file_path.to_normalized_string(),
|
||||
mod_time: get_mod_time(&metadata),
|
||||
})
|
||||
.ok();
|
||||
.ok();
|
||||
|
||||
Continue
|
||||
})
|
||||
@ -130,6 +151,34 @@ where
|
||||
receiver_thread.join().unwrap()
|
||||
}
|
||||
|
||||
fn create_walker<P>(directory: P) -> WalkBuilder
|
||||
where
|
||||
P: AsRef<Path>
|
||||
{
|
||||
let directory: PathBuf = directory.as_ref().into();
|
||||
|
||||
let ignore_glob_set = build_glob_set(&[
|
||||
"**/node_modules",
|
||||
"**/.git",
|
||||
"**/.nx/cache",
|
||||
"**/.nx/workspace-data",
|
||||
"**/.yarn/cache",
|
||||
])
|
||||
.expect("These static ignores always build");
|
||||
|
||||
let mut walker = WalkBuilder::new(&directory);
|
||||
walker.require_git(false);
|
||||
walker.hidden(false);
|
||||
walker.add_custom_ignore_filename(".nxignore");
|
||||
|
||||
// We should make sure to always ignore node_modules and the .git folder
|
||||
walker.filter_entry(move |entry| {
|
||||
let path = entry.path().to_string_lossy();
|
||||
!ignore_glob_set.is_match(path.as_ref())
|
||||
});
|
||||
walker
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::{assert_eq, vec};
|
||||
|
||||
32
packages/nx/src/native/wasi-worker-browser.mjs
Normal file
32
packages/nx/src/native/wasi-worker-browser.mjs
Normal file
@ -0,0 +1,32 @@
|
||||
import { instantiateNapiModuleSync, MessageHandler, WASI } from '@napi-rs/wasm-runtime'
|
||||
|
||||
const handler = new MessageHandler({
|
||||
onLoad({ wasmModule, wasmMemory }) {
|
||||
const wasi = new WASI({
|
||||
print: function () {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log.apply(console, arguments)
|
||||
},
|
||||
printErr: function() {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error.apply(console, arguments)
|
||||
},
|
||||
})
|
||||
return instantiateNapiModuleSync(wasmModule, {
|
||||
childThread: true,
|
||||
wasi,
|
||||
overwriteImports(importObject) {
|
||||
importObject.env = {
|
||||
...importObject.env,
|
||||
...importObject.napi,
|
||||
...importObject.emnapi,
|
||||
memory: wasmMemory,
|
||||
}
|
||||
},
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
globalThis.onmessage = function (e) {
|
||||
handler.handle(e)
|
||||
}
|
||||
63
packages/nx/src/native/wasi-worker.mjs
Normal file
63
packages/nx/src/native/wasi-worker.mjs
Normal file
@ -0,0 +1,63 @@
|
||||
import fs from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import { parse } from "node:path";
|
||||
import { WASI } from "node:wasi";
|
||||
import { parentPort, Worker } from "node:worker_threads";
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
const { instantiateNapiModuleSync, MessageHandler, getDefaultContext } = require("@napi-rs/wasm-runtime");
|
||||
|
||||
if (parentPort) {
|
||||
parentPort.on("message", (data) => {
|
||||
globalThis.onmessage({ data });
|
||||
});
|
||||
}
|
||||
|
||||
Object.assign(globalThis, {
|
||||
self: globalThis,
|
||||
require,
|
||||
Worker,
|
||||
importScripts: function (f) {
|
||||
;(0, eval)(fs.readFileSync(f, "utf8") + "//# sourceURL=" + f);
|
||||
},
|
||||
postMessage: function (msg) {
|
||||
if (parentPort) {
|
||||
parentPort.postMessage(msg);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const emnapiContext = getDefaultContext();
|
||||
|
||||
const __rootDir = parse(process.cwd()).root;
|
||||
|
||||
const handler = new MessageHandler({
|
||||
onLoad({ wasmModule, wasmMemory }) {
|
||||
const wasi = new WASI({
|
||||
version: 'preview1',
|
||||
env: process.env,
|
||||
preopens: {
|
||||
[__rootDir]: __rootDir,
|
||||
},
|
||||
});
|
||||
|
||||
return instantiateNapiModuleSync(wasmModule, {
|
||||
childThread: true,
|
||||
wasi,
|
||||
context: emnapiContext,
|
||||
overwriteImports(importObject) {
|
||||
importObject.env = {
|
||||
...importObject.env,
|
||||
...importObject.napi,
|
||||
...importObject.emnapi,
|
||||
memory: wasmMemory
|
||||
};
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
globalThis.onmessage = function (e) {
|
||||
handler.handle(e);
|
||||
};
|
||||
7
packages/nx/src/native/wasm.rs
Normal file
7
packages/nx/src/native/wasm.rs
Normal file
@ -0,0 +1,7 @@
|
||||
#[napi]
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub const IS_WASM: bool = true;
|
||||
|
||||
#[napi]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub const IS_WASM: bool = false;
|
||||
@ -1,6 +1,5 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use napi::bindgen_prelude::*;
|
||||
use tracing::trace;
|
||||
use watchexec_events::filekind::CreateKind;
|
||||
use watchexec_events::filekind::FileEventKind;
|
||||
@ -127,7 +126,7 @@ pub fn transform_event_to_watch_events(
|
||||
use crate::native::walker::nx_walker_sync;
|
||||
use ignore::gitignore::GitignoreBuilder;
|
||||
use ignore::Match;
|
||||
|
||||
|
||||
if matches!(event_kind, FileEventKind::Create(CreateKind::Folder)) {
|
||||
let mut result = vec![];
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ use napi::bindgen_prelude::External;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::native::hasher::hash;
|
||||
use crate::native::utils::{path::get_child_files, Normalize};
|
||||
use crate::native::utils::{path::get_child_files, Normalize, NxMutex, NxCondvar};
|
||||
use rayon::prelude::*;
|
||||
use std::ops::Deref;
|
||||
use std::path::{Path, PathBuf};
|
||||
@ -12,7 +12,6 @@ use std::thread;
|
||||
use crate::native::logger::enable_logger;
|
||||
use crate::native::project_graph::utils::{find_project_for_path, ProjectRootMappings};
|
||||
use crate::native::types::FileData;
|
||||
use parking_lot::{Condvar, Mutex};
|
||||
use tracing::{trace, warn};
|
||||
|
||||
use crate::native::workspace::files_archive::{read_files_archive, write_files_archive};
|
||||
@ -31,8 +30,32 @@ pub struct WorkspaceContext {
|
||||
|
||||
type Files = Vec<(PathBuf, String)>;
|
||||
|
||||
struct FilesWorker(Option<Arc<(Mutex<Files>, Condvar)>>);
|
||||
fn gather_and_hash_files(workspace_root: &Path, cache_dir: String) -> Vec<(PathBuf, String)>{
|
||||
let archived_files = read_files_archive(&cache_dir);
|
||||
|
||||
trace!("Gathering files in {}", workspace_root.display());
|
||||
let now = std::time::Instant::now();
|
||||
let file_hashes = if let Some(archived_files) = archived_files {
|
||||
selective_files_hash(&workspace_root, archived_files)
|
||||
} else {
|
||||
full_files_hash(&workspace_root)
|
||||
};
|
||||
|
||||
let mut files = file_hashes
|
||||
.iter()
|
||||
.map(|(path, file_hashed)| (PathBuf::from(path), file_hashed.0.to_owned()))
|
||||
.collect::<Vec<_>>();
|
||||
files.par_sort();
|
||||
trace!("hashed and sorted files in {:?}", now.elapsed());
|
||||
|
||||
write_files_archive(&cache_dir, file_hashes);
|
||||
|
||||
files
|
||||
}
|
||||
|
||||
struct FilesWorker(Option<Arc<(NxMutex<Files>, NxCondvar)>>);
|
||||
impl FilesWorker {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn gather_files(workspace_root: &Path, cache_dir: String) -> Self {
|
||||
if !workspace_root.exists() {
|
||||
warn!(
|
||||
@ -42,52 +65,61 @@ impl FilesWorker {
|
||||
return FilesWorker(None);
|
||||
}
|
||||
|
||||
let archived_files = read_files_archive(&cache_dir);
|
||||
|
||||
let files_lock = Arc::new((Mutex::new(Vec::new()), Condvar::new()));
|
||||
let files_lock = Arc::new((NxMutex::new(Vec::new()), NxCondvar::new()));
|
||||
let files_lock_clone = Arc::clone(&files_lock);
|
||||
let workspace_root = workspace_root.to_owned();
|
||||
|
||||
thread::spawn(move || {
|
||||
trace!("locking files");
|
||||
let (lock, cvar) = &*files_lock_clone;
|
||||
let mut workspace_files = lock.lock();
|
||||
let now = std::time::Instant::now();
|
||||
let file_hashes = if let Some(archived_files) = archived_files {
|
||||
selective_files_hash(&workspace_root, archived_files)
|
||||
} else {
|
||||
full_files_hash(&workspace_root)
|
||||
};
|
||||
trace!("Initially locking files");
|
||||
let mut workspace_files = lock.lock().expect("Should be the first time locking files");
|
||||
|
||||
let mut files = file_hashes
|
||||
.iter()
|
||||
.map(|(path, file_hashed)| (PathBuf::from(path), file_hashed.0.to_owned()))
|
||||
.collect::<Vec<_>>();
|
||||
files.par_sort();
|
||||
trace!("hashed and sorted files in {:?}", now.elapsed());
|
||||
let files = gather_and_hash_files(&workspace_root, cache_dir);
|
||||
|
||||
*workspace_files = files;
|
||||
let files_len = workspace_files.len();
|
||||
trace!(?files_len, "files retrieved");
|
||||
|
||||
drop(workspace_files);
|
||||
cvar.notify_all();
|
||||
|
||||
write_files_archive(&cache_dir, file_hashes);
|
||||
});
|
||||
|
||||
FilesWorker(Some(files_lock))
|
||||
}
|
||||
|
||||
pub fn get_files(&self) -> Vec<FileData> {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
fn gather_files(workspace_root: &Path, cache_dir: String) -> Self {
|
||||
if !workspace_root.exists() {
|
||||
warn!(
|
||||
"workspace root does not exist: {}",
|
||||
workspace_root.display()
|
||||
);
|
||||
return FilesWorker(None);
|
||||
}
|
||||
|
||||
let workspace_root = workspace_root.to_owned();
|
||||
|
||||
let files = gather_and_hash_files(&workspace_root, cache_dir);
|
||||
|
||||
trace!("{} files retrieved", files.len());
|
||||
|
||||
let files_lock = Arc::new((NxMutex::new(files), NxCondvar::new()));
|
||||
|
||||
FilesWorker(Some(files_lock))
|
||||
}
|
||||
|
||||
fn get_files(&self) -> Vec<FileData> {
|
||||
if let Some(files_sync) = &self.0 {
|
||||
let (files_lock, cvar) = files_sync.deref();
|
||||
trace!("locking files");
|
||||
let mut files = files_lock.lock();
|
||||
let files_len = files.len();
|
||||
if files_len == 0 {
|
||||
trace!("waiting for files");
|
||||
cvar.wait(&mut files);
|
||||
}
|
||||
|
||||
trace!("waiting for files to be available");
|
||||
let files = files_lock.lock().expect("Should be able to lock files");
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let mut files = cvar.wait(files, |guard| guard.len() == 0).expect("Should be able to wait for files");
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let files = cvar.wait(files, |guard| guard.len() == 0).expect("Should be able to wait for files");
|
||||
|
||||
let file_data = files
|
||||
.iter()
|
||||
@ -118,7 +150,7 @@ impl FilesWorker {
|
||||
};
|
||||
|
||||
let (files_lock, _) = &files_sync.deref();
|
||||
let mut files = files_lock.lock();
|
||||
let mut files = files_lock.lock().expect("Should always be able to update files");
|
||||
let mut map: HashMap<PathBuf, String> = files.drain(..).collect();
|
||||
|
||||
for deleted_path in deleted_files_and_directories {
|
||||
|
||||
@ -11,6 +11,7 @@ use crate::native::workspace::files_archive::{NxFileHashed, NxFileHashes};
|
||||
|
||||
pub fn full_files_hash(workspace_root: &Path) -> NxFileHashes {
|
||||
let files = nx_walker(workspace_root).collect::<Vec<_>>();
|
||||
trace!("Found {} files", files.len());
|
||||
hash_files(files).into_iter().collect()
|
||||
}
|
||||
|
||||
@ -50,15 +51,17 @@ fn hash_files(files: Vec<NxFile>) -> Vec<(String, NxFileHashed)> {
|
||||
let chunks = files.len() / num_parallelism;
|
||||
|
||||
let now = std::time::Instant::now();
|
||||
let files = if chunks < num_parallelism {
|
||||
let files = if cfg!(target_arch = "wasm32") || chunks < num_parallelism {
|
||||
trace!("hashing workspace files in parallel");
|
||||
files
|
||||
.into_iter()
|
||||
.into_par_iter()
|
||||
.filter_map(|file| {
|
||||
hash_file_path(&file.full_path)
|
||||
.map(|hash| (file.normalized_path, NxFileHashed(hash, file.mod_time)))
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
} else {
|
||||
trace!("hashing workspace files in {} chunks of {}", num_parallelism, chunks);
|
||||
files
|
||||
.par_chunks(chunks)
|
||||
.flat_map_iter(|chunks| {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { ChildProcess, RustPseudoTerminal } from '../native';
|
||||
import { ChildProcess, RustPseudoTerminal, IS_WASM } from '../native';
|
||||
import { PseudoIPCServer } from './pseudo-ipc';
|
||||
import { getForkedProcessOsSocketPath } from '../daemon/socket-utils';
|
||||
import { Serializable } from 'child_process';
|
||||
@ -206,6 +206,9 @@ function messageToCode(message: string): number {
|
||||
}
|
||||
|
||||
function supportedPtyPlatform() {
|
||||
if (IS_WASM) {
|
||||
return false;
|
||||
}
|
||||
if (process.platform !== 'win32') {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -57,14 +57,24 @@ export function nxViteBuildCoordinationPlugin(
|
||||
);
|
||||
}
|
||||
|
||||
let firstBuildStart = true;
|
||||
|
||||
return {
|
||||
name: 'nx-vite-build-coordination-plugin',
|
||||
async buildStart() {
|
||||
if (!unregisterFileWatcher) {
|
||||
if (firstBuildStart) {
|
||||
firstBuildStart = false;
|
||||
await buildChangedProjects();
|
||||
unregisterFileWatcher = await createFileWatcher();
|
||||
process.on('exit', () => unregisterFileWatcher());
|
||||
process.on('SIGINT', () => process.exit());
|
||||
if (daemonClient.enabled()) {
|
||||
unregisterFileWatcher = await createFileWatcher();
|
||||
process.on('exit', () => unregisterFileWatcher());
|
||||
process.on('SIGINT', () => process.exit());
|
||||
} else {
|
||||
output.warn({
|
||||
title:
|
||||
'Nx Daemon is not enabled. Projects will not be rebuilt when files change.',
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@ -2,6 +2,7 @@ import { execFileSync, fork } from 'child_process';
|
||||
import * as chalk from 'chalk';
|
||||
import {
|
||||
ExecutorContext,
|
||||
output,
|
||||
parseTargetString,
|
||||
readTargetOptions,
|
||||
} from '@nx/devkit';
|
||||
@ -184,9 +185,13 @@ export default async function* fileServerExecutor(
|
||||
}
|
||||
};
|
||||
|
||||
if (options.watch) {
|
||||
const projectRoot =
|
||||
context.projectsConfigurations.projects[context.projectName].root;
|
||||
if (!daemonClient.enabled() && options.watch) {
|
||||
output.warn({
|
||||
title:
|
||||
'Nx Daemon is not enabled. Static server is not watching for changes.',
|
||||
});
|
||||
}
|
||||
if (daemonClient.enabled() && options.watch) {
|
||||
disposeWatch = await createFileWatcher(context.projectName, run);
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { exec } from 'child_process';
|
||||
import type { Compiler } from 'webpack';
|
||||
import { daemonClient } from 'nx/src/daemon/client/client';
|
||||
import { daemonClient, isDaemonEnabled } from 'nx/src/daemon/client/client';
|
||||
import { BatchFunctionRunner } from 'nx/src/command-line/watch/watch';
|
||||
import { output } from 'nx/src/utils/output';
|
||||
|
||||
@ -12,7 +12,14 @@ export class WebpackNxBuildCoordinationPlugin {
|
||||
if (!skipInitialBuild) {
|
||||
this.buildChangedProjects();
|
||||
}
|
||||
this.startWatchingBuildableLibs();
|
||||
if (isDaemonEnabled()) {
|
||||
this.startWatchingBuildableLibs();
|
||||
} else {
|
||||
output.warn({
|
||||
title:
|
||||
'Nx Daemon is not enabled. Buildable libs will not be rebuilt on file changes.',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
apply(compiler: Compiler) {
|
||||
|
||||
2632
pnpm-lock.yaml
generated
2632
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -1 +1 @@
|
||||
stable
|
||||
stable
|
||||
|
||||
@ -4,7 +4,9 @@ const glob = require('fast-glob');
|
||||
|
||||
const p = process.argv[2];
|
||||
|
||||
const nativeFiles = glob.sync(`packages/${p}/**/*.node`);
|
||||
const nativeFiles = glob.sync(`packages/${p}/**/*.{node,wasm,js,mjs,cjs}`);
|
||||
|
||||
console.log({ nativeFiles });
|
||||
|
||||
nativeFiles.forEach((file) => {
|
||||
fs.copyFileSync(file, `build/${file}`);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user