diff options
author | David Sherret <dsherret@users.noreply.github.com> | 2023-10-25 14:39:00 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-10-25 14:39:00 -0400 |
commit | be97170a193e8cecc5ce03ecd3c1d0add4a06bf7 (patch) | |
tree | fab7d266e208db93dcf0870dda70f7da56ade735 /cli/tests/integration/npm_tests.rs | |
parent | 093b3eee58181ec45839d0fe10b8157326a102b2 (diff) |
feat(unstable): ability to `npm install` then `deno run main.ts` (#20967)
This PR adds a new unstable "bring your own node_modules" (BYONM)
functionality currently behind a `--unstable-byonm` flag (`"unstable":
["byonm"]` in a deno.json).
This enables users to run a separate install command (ex. `npm install`,
`pnpm install`) then run `deno run main.ts` and Deno will respect the
layout of the node_modules directory as setup by the separate install
command. It also works with npm/yarn/pnpm workspaces.
For this PR, the behaviour is opted into by specifying
`--unstable-byonm`/`"unstable": ["byonm"]`, but in the future we may
make this the default behaviour as outlined in
https://github.com/denoland/deno/issues/18967#issuecomment-1761248941
This is an extremely rough initial implementation. Errors are
terrible in this and the LSP requires frequent restarts. Improvements
will be done in follow up PRs.
Diffstat (limited to 'cli/tests/integration/npm_tests.rs')
-rw-r--r-- | cli/tests/integration/npm_tests.rs | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/cli/tests/integration/npm_tests.rs b/cli/tests/integration/npm_tests.rs index ddbc79cec..c3fe2949c 100644 --- a/cli/tests/integration/npm_tests.rs +++ b/cli/tests/integration/npm_tests.rs @@ -2219,3 +2219,240 @@ itest!(require_resolve_url_paths { cwd: Some("npm/require_resolve_url/"), copy_temp_dir: Some("npm/require_resolve_url/"), }); + +#[test] +pub fn byonm_cjs_esm_packages() { + let test_context = TestContextBuilder::for_npm() + .env("DENO_UNSTABLE_BYONM", "1") + .use_temp_cwd() + .build(); + let dir = test_context.temp_dir(); + let run_npm = |args: &str| { + test_context + .new_command() + .name("npm") + .args(args) + .run() + .skip_output_check(); + }; + + run_npm("init -y"); + run_npm("install @denotest/esm-basic @denotest/cjs-default-export @denotest/dual-cjs-esm chalk@4 chai@4.3"); + + dir.write( + "main.ts", + r#" +import { getValue, setValue } from "@denotest/esm-basic"; + +setValue(2); +console.log(getValue()); + +import cjsDefault from "@denotest/cjs-default-export"; +console.log(cjsDefault.default()); +console.log(cjsDefault.named()); + +import { getKind } from "@denotest/dual-cjs-esm"; +console.log(getKind()); + + +"#, + ); + let output = test_context.new_command().args("run --check main.ts").run(); + output + .assert_matches_text("Check file:///[WILDCARD]/main.ts\n2\n1\n2\nesm\n"); + + // should not have created the .deno directory + assert!(!dir.path().join("node_modules/.deno").exists()); + + // try chai + dir.write( + "chai.ts", + r#"import { expect } from "chai"; + + const timeout = setTimeout(() => {}, 0); + expect(timeout).to.be.a("number"); + clearTimeout(timeout);"#, + ); + test_context.new_command().args("run chai.ts").run(); + + // try chalk cjs + dir.write( + "chalk.ts", + "import chalk from 'chalk'; console.log(chalk.green('chalk cjs loads'));", + ); + let output = test_context + .new_command() + .args("run --allow-read chalk.ts") + .run(); + output.assert_matches_text("chalk cjs loads\n"); + + // try using an npm specifier for chalk that matches the version we installed + dir.write( + "chalk.ts", + "import chalk from 'npm:chalk@4'; console.log(chalk.green('chalk cjs loads'));", + ); + let output = test_context + .new_command() + .args("run --allow-read chalk.ts") + .run(); + output.assert_matches_text("chalk cjs loads\n"); + + // try with one that doesn't match the package.json + dir.write( + "chalk.ts", + "import chalk from 'npm:chalk@5'; console.log(chalk.green('chalk cjs loads'));", + ); + let output = test_context + .new_command() + .args("run --allow-read chalk.ts") + .run(); + output.assert_matches_text( + r#"error: Could not find a matching package for 'npm:chalk@5' in '[WILDCARD]package.json'. You must specify this as a package.json dependency when the node_modules folder is not managed by Deno. + at file:///[WILDCARD]chalk.ts:1:19 +"#); + output.assert_exit_code(1); +} + +#[test] +pub fn byonm_package_npm_specifier_not_installed_and_invalid_subpath() { + let test_context = TestContextBuilder::for_npm() + .env("DENO_UNSTABLE_BYONM", "1") + .use_temp_cwd() + .build(); + let dir = test_context.temp_dir(); + dir.path().join("package.json").write_json(&json!({ + "dependencies": { + "chalk": "4", + "@denotest/conditional-exports-strict": "1" + } + })); + dir.write( + "main.ts", + "import chalk from 'npm:chalk'; console.log(chalk.green('hi'));", + ); + + // no npm install has been run, so this should give an informative error + let output = test_context.new_command().args("run main.ts").run(); + output.assert_matches_text( + r#"error: Could not find '[WILDCARD]package.json'. Maybe run `npm install`? + at file:///[WILDCARD]/main.ts:1:19 +"#, + ); + output.assert_exit_code(1); + + // now test for an invalid sub path after doing an npm install + dir.write( + "main.ts", + "import 'npm:@denotest/conditional-exports-strict/test';", + ); + + test_context + .new_command() + .name("npm") + .args("install") + .run() + .skip_output_check(); + + let output = test_context.new_command().args("run main.ts").run(); + output.assert_matches_text( + r#"error: Failed resolving package subpath './test' for '[WILDCARD]package.json' + at file:///[WILDCARD]/main.ts:1:8 +"#, + ); + output.assert_exit_code(1); +} + +#[test] +pub fn byonm_npm_workspaces() { + let test_context = TestContextBuilder::for_npm().use_temp_cwd().build(); + let dir = test_context.temp_dir(); + dir.write( + "deno.json", + r#"{ + "unstable": [ + "byonm" + ] + }"#, + ); + + dir.write( + "package.json", + r#"{ + "name": "my-workspace", + "workspaces": [ + "project-a", + "project-b" + ] +} +"#, + ); + + let project_a_dir = dir.path().join("project-a"); + project_a_dir.create_dir_all(); + project_a_dir.join("package.json").write_json(&json!({ + "name": "project-a", + "version": "1.0.0", + "main": "./index.js", + "type": "module", + "dependencies": { + "chai": "^4.2", + "project-b": "^1" + } + })); + project_a_dir.join("index.js").write( + r#" +import { expect } from "chai"; + +const timeout = setTimeout(() => {}, 0); +expect(timeout).to.be.a("number"); +clearTimeout(timeout); + +export function add(a, b) { + return a + b; +} +"#, + ); + project_a_dir + .join("index.d.ts") + .write("export function add(a: number, b: number): number;"); + + let project_b_dir = dir.path().join("project-b"); + project_b_dir.create_dir_all(); + project_b_dir.join("package.json").write_json(&json!({ + "name": "project-b", + "version": "1.0.0", + "type": "module", + "dependencies": { + "@denotest/esm-basic": "^1.0", + } + })); + project_b_dir.join("main.ts").write( + r#" +import { getValue, setValue } from "@denotest/esm-basic"; + +setValue(5); +console.log(getValue()); + +import { add } from "project-a"; +console.log(add(1, 2)); +"#, + ); + + test_context + .new_command() + .name("npm") + .args("install") + .run() + .skip_output_check(); + + let output = test_context + .new_command() + .args("run ./project-b/main.ts") + .run(); + output.assert_matches_text("5\n3\n"); + let output = test_context + .new_command() + .args("check ./project-b/main.ts") + .run(); + output.assert_matches_text("Check file:///[WILDCARD]/project-b/main.ts\n"); +} |