@joshmossas/nx-cargo 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/.babelrc +3 -0
  2. package/.eslintrc.json +34 -0
  3. package/README.md +39 -0
  4. package/build.config.ts +10 -0
  5. package/dist/index.cjs +100 -0
  6. package/dist/index.d.cts +5 -0
  7. package/dist/index.d.mts +5 -0
  8. package/dist/index.d.ts +5 -0
  9. package/dist/index.mjs +79 -0
  10. package/executors.json +25 -0
  11. package/generators.json +25 -0
  12. package/jest.config.ts +17 -0
  13. package/package.json +30 -0
  14. package/project.json +53 -0
  15. package/src/common/index.spec.ts +183 -0
  16. package/src/common/index.ts +358 -0
  17. package/src/common/schema.d.ts +111 -0
  18. package/src/executors/build/executor.ts +21 -0
  19. package/src/executors/build/schema.d.ts +39 -0
  20. package/src/executors/build/schema.json +77 -0
  21. package/src/executors/clippy/executor.ts +18 -0
  22. package/src/executors/clippy/schema.d.ts +34 -0
  23. package/src/executors/clippy/schema.json +29 -0
  24. package/src/executors/run/executor.ts +19 -0
  25. package/src/executors/run/schema.d.ts +32 -0
  26. package/src/executors/run/schema.json +77 -0
  27. package/src/executors/test/executor.ts +19 -0
  28. package/src/executors/test/schema.d.ts +22 -0
  29. package/src/executors/test/schema.json +73 -0
  30. package/src/generators/binary/files/Cargo.toml__template__ +8 -0
  31. package/src/generators/binary/files/src/main.rs__template__ +3 -0
  32. package/src/generators/binary/generator.spec.ts +75 -0
  33. package/src/generators/binary/generator.ts +76 -0
  34. package/src/generators/binary/schema.d.ts +6 -0
  35. package/src/generators/binary/schema.json +35 -0
  36. package/src/generators/init/files/Cargo.toml +2 -0
  37. package/src/generators/init/files/rust-toolchain.toml__template__ +2 -0
  38. package/src/generators/init/files/rustfmt.toml +0 -0
  39. package/src/generators/init/generator.spec.ts +49 -0
  40. package/src/generators/init/generator.ts +55 -0
  41. package/src/generators/init/schema.d.ts +7 -0
  42. package/src/generators/init/schema.json +14 -0
  43. package/src/generators/library/files/Cargo.toml__template__ +8 -0
  44. package/src/generators/library/files/src/lib.rs__template__ +13 -0
  45. package/src/generators/library/generator.spec.ts +96 -0
  46. package/src/generators/library/generator.ts +78 -0
  47. package/src/generators/library/schema.d.ts +6 -0
  48. package/src/generators/library/schema.json +35 -0
  49. package/src/graph/index.ts +189 -0
  50. package/src/index.ts +1 -0
  51. package/tsconfig.json +13 -0
  52. package/tsconfig.lib.json +12 -0
  53. package/tsconfig.spec.json +20 -0
package/.babelrc ADDED
@@ -0,0 +1,3 @@
1
+ {
2
+ "presets": [["@nx/js/babel", { "useBuiltIns": "usage" }]]
3
+ }
package/.eslintrc.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "extends": ["../../.eslintrc.json"],
3
+ "ignorePatterns": ["!**/*"],
4
+ "overrides": [
5
+ {
6
+ "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7
+ "rules": {
8
+ "@typescript-eslint/no-explicit-any": "off"
9
+ }
10
+ },
11
+ {
12
+ "files": ["*.ts", "*.tsx"],
13
+ "rules": {}
14
+ },
15
+ {
16
+ "files": ["*.js", "*.jsx"],
17
+ "rules": {}
18
+ },
19
+ {
20
+ "files": ["*.json"],
21
+ "parser": "jsonc-eslint-parser",
22
+ "rules": {
23
+ "@nx/dependency-checks": "error"
24
+ }
25
+ },
26
+ {
27
+ "files": ["./package.json", "./executors.json"],
28
+ "parser": "jsonc-eslint-parser",
29
+ "rules": {
30
+ "@nx/nx-plugin-checks": "error"
31
+ }
32
+ }
33
+ ]
34
+ }
package/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # @nxrs/cargo
2
+
3
+ [![npm version](https://badge.fury.io/js/%40nxrs%2Fcargo.svg)](https://badge.fury.io/js/%40nxrs%2Fcargo)
4
+
5
+ This is a WIP plugin for `@nrwl/nx` adding support for Rust applications and libraries.
6
+
7
+ ## Generators
8
+
9
+ ```sh
10
+ # @nxrs/cargo:bin also works
11
+ > nx generate @nxrs/cargo:app my-rust-app
12
+ ```
13
+
14
+ ```sh
15
+ > nx generate @nxrs/cargo:lib my-rust-lib
16
+ ```
17
+
18
+ ## Executors
19
+
20
+ ```sh
21
+ # Build a library or binary
22
+ > nx build my-rust-app
23
+
24
+ # Run unit tests in a library
25
+ > nx test my-rust-lib
26
+
27
+ # Check a Rust project with `clippy`
28
+ > nx lint my-rust-app
29
+ # Don't fail on warnings:
30
+ > nx lint my-rust-app --fail-on-warnings false
31
+ ```
32
+
33
+ ### Options
34
+
35
+ The executors accept most of the same CLI args as the corresponding `cargo` commands. When in doubt, run with the `--help` flag to see all options with descriptions:
36
+
37
+ ```sh
38
+ > nx build my-rust-app --help
39
+ ```
@@ -0,0 +1,10 @@
1
+ import { defineBuildConfig } from "unbuild";
2
+
3
+ export default defineBuildConfig({
4
+ entries: ["src/index.ts"],
5
+ outDir: "dist",
6
+ declaration: true,
7
+ rollup: {
8
+ emitCJS: true,
9
+ }
10
+ });
package/dist/index.cjs ADDED
@@ -0,0 +1,100 @@
1
+ 'use strict';
2
+
3
+ const devkit = require('@nx/devkit');
4
+ const fs = require('node:fs');
5
+ const cp = require('node:child_process');
6
+ const os = require('node:os');
7
+ const path = require('node:path');
8
+
9
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
10
+
11
+ function _interopNamespaceCompat(e) {
12
+ if (e && typeof e === 'object' && 'default' in e) return e;
13
+ const n = Object.create(null);
14
+ if (e) {
15
+ for (const k in e) {
16
+ n[k] = e[k];
17
+ }
18
+ }
19
+ n.default = e;
20
+ return n;
21
+ }
22
+
23
+ const fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
24
+ const cp__namespace = /*#__PURE__*/_interopNamespaceCompat(cp);
25
+ const os__namespace = /*#__PURE__*/_interopNamespaceCompat(os);
26
+ const path__namespace = /*#__PURE__*/_interopNamespaceCompat(path);
27
+
28
+ function createDependencies(_, ctx) {
29
+ const allDependencies = [];
30
+ const processedWorkspaceRoots = /* @__PURE__ */ new Set();
31
+ const cargoConfigPaths = Object.values(ctx.projects).map((p) => path__namespace.join(ctx.workspaceRoot, p.root, "Cargo.toml")).filter((p) => fs__default.existsSync(p));
32
+ for (const configPath of cargoConfigPaths) {
33
+ const configDir = path__namespace.dirname(configPath);
34
+ const metadata = getCargoMetadata(configDir);
35
+ if (processedWorkspaceRoots.has(metadata.workspace_root)) {
36
+ continue;
37
+ }
38
+ processedWorkspaceRoots.add(metadata.workspace_root);
39
+ const workspaceDeps = processWorkspaceMetadata(ctx, metadata);
40
+ allDependencies.push(...workspaceDeps);
41
+ }
42
+ return allDependencies;
43
+ }
44
+ function processWorkspaceMetadata(ctx, metadata) {
45
+ const {
46
+ packages,
47
+ workspace_members: cargoWsMembers,
48
+ resolve: cargoResolve
49
+ } = metadata;
50
+ const workspacePackages = /* @__PURE__ */ new Map();
51
+ for (const id of cargoWsMembers) {
52
+ const pkg = packages.find((p) => p.id === id);
53
+ if (pkg) workspacePackages.set(id, pkg);
54
+ }
55
+ const nxData = mapCargoProjects(ctx, workspacePackages);
56
+ return cargoResolve.nodes.filter(({ id }) => nxData.has(id)).flatMap(({ id: sourceId, dependencies }) => {
57
+ const sourceProject = nxData.get(sourceId);
58
+ const cargoPackage = workspacePackages.get(sourceId);
59
+ const sourceManifest = path__namespace.relative(ctx.workspaceRoot, cargoPackage.manifest_path).replace(/\\/g, "/");
60
+ return dependencies.filter((depId) => nxData.has(depId)).map((depId) => ({
61
+ source: sourceProject.name,
62
+ target: nxData.get(depId).name,
63
+ type: devkit.DependencyType.static,
64
+ sourceFile: sourceManifest
65
+ }));
66
+ });
67
+ }
68
+ function getCargoMetadata(cwd) {
69
+ const availableMemory = os__namespace.freemem();
70
+ const metadata = cp__namespace.execSync("cargo metadata --format-version=1", {
71
+ encoding: "utf8",
72
+ maxBuffer: availableMemory,
73
+ cwd
74
+ // Crucial: run in the workspace directory
75
+ });
76
+ return JSON.parse(metadata);
77
+ }
78
+ function mapCargoProjects(ctx, packages) {
79
+ let result = /* @__PURE__ */ new Map();
80
+ for (let [cargoId, cargoPackage] of packages) {
81
+ if (!cargoPackage.manifest_path) {
82
+ throw new Error("Expected cargo package's `manifest_path` to exist");
83
+ }
84
+ let manifestDir = path__namespace.dirname(cargoPackage.manifest_path);
85
+ let projectDir = path__namespace.relative(ctx.workspaceRoot, manifestDir).replace(/\\/g, "/");
86
+ let found = Object.entries(ctx.projects).find(
87
+ ([, config]) => config.root === projectDir
88
+ );
89
+ if (found) {
90
+ let [projectName, projectConfig] = found;
91
+ result.set(cargoId, {
92
+ ...projectConfig,
93
+ name: projectName
94
+ });
95
+ }
96
+ }
97
+ return result;
98
+ }
99
+
100
+ exports.createDependencies = createDependencies;
@@ -0,0 +1,5 @@
1
+ import { CreateDependenciesContext, RawProjectGraphDependency } from '@nx/devkit';
2
+
3
+ declare function createDependencies(_: unknown, ctx: CreateDependenciesContext): RawProjectGraphDependency[];
4
+
5
+ export { createDependencies };
@@ -0,0 +1,5 @@
1
+ import { CreateDependenciesContext, RawProjectGraphDependency } from '@nx/devkit';
2
+
3
+ declare function createDependencies(_: unknown, ctx: CreateDependenciesContext): RawProjectGraphDependency[];
4
+
5
+ export { createDependencies };
@@ -0,0 +1,5 @@
1
+ import { CreateDependenciesContext, RawProjectGraphDependency } from '@nx/devkit';
2
+
3
+ declare function createDependencies(_: unknown, ctx: CreateDependenciesContext): RawProjectGraphDependency[];
4
+
5
+ export { createDependencies };
package/dist/index.mjs ADDED
@@ -0,0 +1,79 @@
1
+ import { DependencyType } from '@nx/devkit';
2
+ import fs from 'node:fs';
3
+ import * as cp from 'node:child_process';
4
+ import * as os from 'node:os';
5
+ import * as path from 'node:path';
6
+
7
+ function createDependencies(_, ctx) {
8
+ const allDependencies = [];
9
+ const processedWorkspaceRoots = /* @__PURE__ */ new Set();
10
+ const cargoConfigPaths = Object.values(ctx.projects).map((p) => path.join(ctx.workspaceRoot, p.root, "Cargo.toml")).filter((p) => fs.existsSync(p));
11
+ for (const configPath of cargoConfigPaths) {
12
+ const configDir = path.dirname(configPath);
13
+ const metadata = getCargoMetadata(configDir);
14
+ if (processedWorkspaceRoots.has(metadata.workspace_root)) {
15
+ continue;
16
+ }
17
+ processedWorkspaceRoots.add(metadata.workspace_root);
18
+ const workspaceDeps = processWorkspaceMetadata(ctx, metadata);
19
+ allDependencies.push(...workspaceDeps);
20
+ }
21
+ return allDependencies;
22
+ }
23
+ function processWorkspaceMetadata(ctx, metadata) {
24
+ const {
25
+ packages,
26
+ workspace_members: cargoWsMembers,
27
+ resolve: cargoResolve
28
+ } = metadata;
29
+ const workspacePackages = /* @__PURE__ */ new Map();
30
+ for (const id of cargoWsMembers) {
31
+ const pkg = packages.find((p) => p.id === id);
32
+ if (pkg) workspacePackages.set(id, pkg);
33
+ }
34
+ const nxData = mapCargoProjects(ctx, workspacePackages);
35
+ return cargoResolve.nodes.filter(({ id }) => nxData.has(id)).flatMap(({ id: sourceId, dependencies }) => {
36
+ const sourceProject = nxData.get(sourceId);
37
+ const cargoPackage = workspacePackages.get(sourceId);
38
+ const sourceManifest = path.relative(ctx.workspaceRoot, cargoPackage.manifest_path).replace(/\\/g, "/");
39
+ return dependencies.filter((depId) => nxData.has(depId)).map((depId) => ({
40
+ source: sourceProject.name,
41
+ target: nxData.get(depId).name,
42
+ type: DependencyType.static,
43
+ sourceFile: sourceManifest
44
+ }));
45
+ });
46
+ }
47
+ function getCargoMetadata(cwd) {
48
+ const availableMemory = os.freemem();
49
+ const metadata = cp.execSync("cargo metadata --format-version=1", {
50
+ encoding: "utf8",
51
+ maxBuffer: availableMemory,
52
+ cwd
53
+ // Crucial: run in the workspace directory
54
+ });
55
+ return JSON.parse(metadata);
56
+ }
57
+ function mapCargoProjects(ctx, packages) {
58
+ let result = /* @__PURE__ */ new Map();
59
+ for (let [cargoId, cargoPackage] of packages) {
60
+ if (!cargoPackage.manifest_path) {
61
+ throw new Error("Expected cargo package's `manifest_path` to exist");
62
+ }
63
+ let manifestDir = path.dirname(cargoPackage.manifest_path);
64
+ let projectDir = path.relative(ctx.workspaceRoot, manifestDir).replace(/\\/g, "/");
65
+ let found = Object.entries(ctx.projects).find(
66
+ ([, config]) => config.root === projectDir
67
+ );
68
+ if (found) {
69
+ let [projectName, projectConfig] = found;
70
+ result.set(cargoId, {
71
+ ...projectConfig,
72
+ name: projectName
73
+ });
74
+ }
75
+ }
76
+ return result;
77
+ }
78
+
79
+ export { createDependencies };
package/executors.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "executors": {
4
+ "build": {
5
+ "implementation": "./src/executors/build/executor",
6
+ "schema": "./src/executors/build/schema.json",
7
+ "description": "Build a Cargo project"
8
+ },
9
+ "run": {
10
+ "implementation": "./src/executors/run/executor",
11
+ "schema": "./src/executors/run/schema.json",
12
+ "description": "Run a Cargo executable"
13
+ },
14
+ "test": {
15
+ "implementation": "./src/executors/test/executor",
16
+ "schema": "./src/executors/test/schema.json",
17
+ "description": "Run unit tests"
18
+ },
19
+ "clippy": {
20
+ "implementation": "./src/executors/clippy/executor",
21
+ "schema": "./src/executors/clippy/schema.json",
22
+ "description": "Linter for Rust code"
23
+ }
24
+ }
25
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "name": "cargo",
4
+ "version": "0.0.1",
5
+ "generators": {
6
+ "init": {
7
+ "factory": "./src/generators/init/generator",
8
+ "schema": "./src/generators/init/schema.json",
9
+ "description": "Initialize the @nxrs/cargo plugin",
10
+ "hidden": true
11
+ },
12
+ "binary": {
13
+ "factory": "./src/generators/binary/generator",
14
+ "schema": "./src/generators/binary/schema.json",
15
+ "description": "Create a Rust binary",
16
+ "aliases": ["bin", "app", "application"]
17
+ },
18
+ "library": {
19
+ "factory": "./src/generators/library/generator",
20
+ "schema": "./src/generators/library/schema.json",
21
+ "description": "Create a Rust library",
22
+ "aliases": ["lib"]
23
+ }
24
+ }
25
+ }
package/jest.config.ts ADDED
@@ -0,0 +1,17 @@
1
+ /* eslint-disable */
2
+ export default {
3
+ displayName: "cargo",
4
+ preset: "../../jest.preset.js",
5
+ globals: {},
6
+ testEnvironment: "node",
7
+ transform: {
8
+ "^.+\\.[tj]sx?$": [
9
+ "ts-jest",
10
+ {
11
+ tsconfig: "<rootDir>/tsconfig.spec.json",
12
+ },
13
+ ],
14
+ },
15
+ moduleFileExtensions: ["ts", "tsx", "js", "jsx"],
16
+ coverageDirectory: "../../coverage/packages/cargo",
17
+ };
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@joshmossas/nx-cargo",
3
+ "private": false,
4
+ "version": "0.6.2",
5
+ "license": "MIT",
6
+ "main": "dist/index.mjs",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/nxrs/cargo.git"
10
+ },
11
+ "author": {
12
+ "name": "Danny McGee",
13
+ "email": "dannymcgee@gmail.com",
14
+ "url": "https://github.com/dannymcgee"
15
+ },
16
+ "dependencies": {
17
+ "chalk": "^4.1.2",
18
+ "lodash": "^4.17.21",
19
+ "tslib": "^2.0.0"
20
+ },
21
+ "peerDependencies": {
22
+ "@nx/devkit": ">= 17"
23
+ },
24
+ "devDependencies": {
25
+ "unbuild": "^3.6.1"
26
+ },
27
+ "scripts": {
28
+ "build": "unbuild"
29
+ }
30
+ }
package/project.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
3
+ "name": "cargo",
4
+ "projectType": "library",
5
+ "sourceRoot": "packages/cargo/src",
6
+ "tags": [],
7
+ "targets": {
8
+ "lint": {
9
+ "executor": "@nx/eslint:lint",
10
+ "outputs": ["{options.outputFile}"]
11
+ },
12
+ "test": {
13
+ "executor": "@nx/jest:jest",
14
+ "outputs": ["{workspaceRoot}/coverage/packages/cargo"],
15
+ "options": {
16
+ "jestConfig": "packages/cargo/jest.config.ts"
17
+ }
18
+ },
19
+ "build": {
20
+ "executor": "@nx/js:tsc",
21
+ "outputs": ["{options.outputPath}"],
22
+ "options": {
23
+ "outputPath": "dist/packages/cargo",
24
+ "tsConfig": "packages/cargo/tsconfig.lib.json",
25
+ "packageJson": "packages/cargo/package.json",
26
+ "main": "packages/cargo/src/index.ts",
27
+ "assets": [
28
+ "packages/cargo/*.md",
29
+ {
30
+ "input": "./packages/cargo/src",
31
+ "glob": "**/!(*.ts)",
32
+ "output": "./src"
33
+ },
34
+ {
35
+ "input": "./packages/cargo/src",
36
+ "glob": "**/*.d.ts",
37
+ "output": "./src"
38
+ },
39
+ {
40
+ "input": "./packages/cargo",
41
+ "glob": "generators.json",
42
+ "output": "."
43
+ },
44
+ {
45
+ "input": "./packages/cargo",
46
+ "glob": "executors.json",
47
+ "output": "."
48
+ }
49
+ ]
50
+ }
51
+ }
52
+ }
53
+ }
@@ -0,0 +1,183 @@
1
+ import { ExecutorContext, Tree } from "@nx/devkit";
2
+ import { createTreeWithEmptyWorkspace } from "@nx/devkit/testing";
3
+
4
+ import ClippyCliOptions from "../executors/clippy/schema";
5
+ import {
6
+ CargoOptions,
7
+ normalizeGeneratorOptions,
8
+ parseCargoArgs,
9
+ Target,
10
+ } from "./index";
11
+
12
+ describe("common utils", () => {
13
+ describe("parseCargoArgs", () => {
14
+ it("should support --target argument", () => {
15
+ let ctx = mockExecutorContext("test-app:build");
16
+ let opts: CargoOptions = {
17
+ target: "86_64-pc-windows-gnu",
18
+ };
19
+ let [args] = parseCargoArgs(Target.Build, opts, ctx);
20
+ args.unshift("cargo");
21
+
22
+ expect(args.join(" ")).toEqual(
23
+ "cargo build --bin test-app --target 86_64-pc-windows-gnu"
24
+ );
25
+ });
26
+
27
+ it("should support --package argument", () => {
28
+ let ctx = mockExecutorContext("test-app:build");
29
+ let opts: CargoOptions = {
30
+ package: "foo",
31
+ };
32
+
33
+ let args = ["cargo", ...parseCargoArgs(Target.Build, opts, ctx)[0]];
34
+ expect(args.join(" ")).toEqual("cargo build --bin foo");
35
+
36
+ args = ["cargo", ...parseCargoArgs(Target.Test, opts, ctx)[0]];
37
+ expect(args.join(" ")).toEqual("cargo test -p foo");
38
+
39
+ args = ["cargo", ...parseCargoArgs(Target.Run, opts, ctx)[0]];
40
+ expect(args.join(" ")).toEqual("cargo run -p foo");
41
+
42
+ args = ["cargo", ...parseCargoArgs(Target.Clippy, opts, ctx)[0]];
43
+ expect(args.join(" ")).toEqual(
44
+ "cargo clippy -p foo -- -D warnings --no-deps"
45
+ );
46
+ });
47
+
48
+ it("should ignore the Nx-config-specified target name", () => {
49
+ let ctx = mockExecutorContext("test-app:flooptydoopty");
50
+ let opts: CargoOptions = {};
51
+ let args = ["cargo", ...parseCargoArgs(Target.Build, opts, ctx)[0]];
52
+
53
+ expect(args.join(" ")).toEqual("cargo build --bin test-app");
54
+ });
55
+
56
+ it("should correctly handle `release` option", () => {
57
+ let ctx = mockExecutorContext("test-app:build");
58
+
59
+ let args = ["cargo", ...parseCargoArgs(Target.Build, { release: false }, ctx)[0]];
60
+ expect(args.join(" ")).toEqual("cargo build --bin test-app");
61
+
62
+ args = ["cargo", ...parseCargoArgs(Target.Build, { release: true }, ctx)[0]];
63
+ expect(args.join(" ")).toEqual("cargo build --bin test-app --profile release");
64
+ });
65
+
66
+ it("should pass through unknown arguments to cargo", () => {
67
+ let ctx = mockExecutorContext("test-app:build");
68
+
69
+ let opts: CargoOptions & { [key: string]: string } = {
70
+ profile: "dev-custom",
71
+ };
72
+ let args = ["cargo", ...parseCargoArgs(Target.Build, opts, ctx)[0]];
73
+
74
+ expect(args.join(" ")).toEqual(
75
+ "cargo build --bin test-app --profile dev-custom",
76
+ );
77
+
78
+ opts = { unknownArg: "lorem-ipsum" };
79
+ args = ["cargo", ...parseCargoArgs(Target.Build, opts, ctx)[0]];
80
+
81
+ expect(args.join(" ")).toEqual(
82
+ "cargo build --bin test-app --unknown-arg lorem-ipsum",
83
+ );
84
+ });
85
+
86
+ it("allows specifying a custom binary target", () => {
87
+ let ctx = mockExecutorContext("test-app:build");
88
+
89
+ let opts: CargoOptions = {
90
+ bin: "custom-bin-name",
91
+ };
92
+ let args = ["cargo", ...parseCargoArgs(Target.Build, opts, ctx)[0]];
93
+
94
+ expect(args.join(" ")).toEqual(
95
+ "cargo build -p test-app --bin custom-bin-name",
96
+ );
97
+ });
98
+
99
+ it("correctly handles pass-through arguments for clippy", () => {
100
+ let ctx = mockExecutorContext("test-app:lint");
101
+ let opts: ClippyCliOptions = {
102
+ package: "test-app-pkg",
103
+ target: "wasm32-unknown-unknown",
104
+ fix: false,
105
+ failOnWarnings: true,
106
+ noDeps: true,
107
+ };
108
+ let args = ["cargo", ...parseCargoArgs(Target.Clippy, opts, ctx)[0]];
109
+
110
+ expect(args.join(" ")).toEqual(
111
+ "cargo clippy -p test-app-pkg --target wasm32-unknown-unknown "
112
+ + "-- -D warnings --no-deps",
113
+ );
114
+ });
115
+
116
+ it("correctly handles the `env` option", () => {
117
+ let ctx = mockExecutorContext("test-app:run");
118
+ let opts: CargoOptions = {
119
+ package: "foo",
120
+ env: {
121
+ RUST_BACKTRACE: "FULL",
122
+ },
123
+ };
124
+ let [args, env] = parseCargoArgs(Target.Run, opts, ctx);
125
+
126
+ expect(["cargo", ...args].join(" ")).toEqual("cargo run -p foo");
127
+ expect(env).toHaveProperty("RUST_BACKTRACE", "FULL");
128
+ });
129
+ });
130
+
131
+ describe("normalizeGeneratorOptions", () => {
132
+ let appTree: Tree;
133
+
134
+ beforeAll(() => {
135
+ appTree = createTreeWithEmptyWorkspace();
136
+ });
137
+
138
+ it("should respect kebab-case project names", () => {
139
+ let opts = normalizeGeneratorOptions("application", appTree, { name: "my-app" });
140
+ expect(opts.projectName).toBe("my-app");
141
+ });
142
+
143
+ it("should respect snake_case project names", () => {
144
+ let opts = normalizeGeneratorOptions("application", appTree, { name: "my_app" });
145
+ expect(opts.projectName).toBe("my_app");
146
+ });
147
+
148
+ it("should respect PascalCase project names", () => {
149
+ let opts = normalizeGeneratorOptions("application", appTree, { name: "MyApp" });
150
+ expect(opts.projectName).toBe("MyApp");
151
+ });
152
+
153
+ it("should respect camelCase project names (you monster)", () => {
154
+ let opts = normalizeGeneratorOptions("application", appTree, { name: "myApp" });
155
+ expect(opts.projectName).toBe("myApp");
156
+ });
157
+ });
158
+ });
159
+
160
+ function mockExecutorContext(command: string): ExecutorContext {
161
+ let [projectName, targetName] = command.split(":");
162
+
163
+ return {
164
+ cwd: "C:/test",
165
+ root: "C:/test",
166
+ isVerbose: false,
167
+ workspace: {
168
+ projects: {
169
+ "test-app": {
170
+ root: "apps/test-app",
171
+ projectType: "application",
172
+ },
173
+ "test-lib": {
174
+ root: "libs/test-lib",
175
+ projectType: "library",
176
+ },
177
+ },
178
+ version: 2,
179
+ },
180
+ projectName,
181
+ targetName,
182
+ };
183
+ }