diff --git a/.changeset/bright-pigs-deliver.md b/.changeset/bright-pigs-deliver.md new file mode 100644 index 000000000..5e2dd8f01 --- /dev/null +++ b/.changeset/bright-pigs-deliver.md @@ -0,0 +1,5 @@ +--- +"create-eth": patch +--- + +validate project name inline with npm name rules diff --git a/package.json b/package.json index ef5a95f92..77a49e0f1 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "@types/inquirer": "9.0.3", "@types/ncp": "2.0.5", "@types/node": "18.16.0", + "@types/validate-npm-package-name": "4.0.2", "eslint": "^9.15.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.2.1", @@ -58,7 +59,8 @@ "inquirer": "9.2.0", "listr2": "^8.2.1", "merge-packages": "^0.1.6", - "ncp": "2.0.0" + "ncp": "2.0.0", + "validate-npm-package-name": "6.0.0" }, "packageManager": "yarn@3.5.0" } diff --git a/src/utils/parse-arguments-into-options.ts b/src/utils/parse-arguments-into-options.ts index aaabaef1d..3d9b631e0 100644 --- a/src/utils/parse-arguments-into-options.ts +++ b/src/utils/parse-arguments-into-options.ts @@ -12,6 +12,7 @@ import { validateFoundryUp } from "./system-validation"; import fs from "fs"; import path from "path"; import { fileURLToPath } from "url"; +import { validateNpmName } from "./validate-name"; const validateExternalExtension = async ( extensionName: string, @@ -74,7 +75,7 @@ export async function parseArgumentsIntoOptions( "-h": "--help", }, { - argv: rawArgs.slice(2).map(a => a.toLowerCase()), + argv: rawArgs.slice(2), }, ); @@ -84,7 +85,7 @@ export async function parseArgumentsIntoOptions( const help = args["--help"] ?? false; - const project = args._[0] ?? null; + let project: string | null = args._[0] ?? null; // use the original extension arg const extensionName = args["--extension"] && rawArgs.slice(2).find(a => a.toLowerCase() === args["--extension"]); @@ -101,6 +102,18 @@ export async function parseArgumentsIntoOptions( ); } + if (project) { + const validation = validateNpmName(project); + if (!validation.valid) { + console.error( + `Could not create a project called ${chalk.yellow(`"${project}"`)} because of naming restrictions:`, + ); + + validation.problems.forEach(p => console.error(`${chalk.red(">>")} Project ${p}`)); + project = null; + } + } + let solidityFrameworkChoices = [ SOLIDITY_FRAMEWORKS.HARDHAT, SOLIDITY_FRAMEWORKS.FOUNDRY, diff --git a/src/utils/prompt-for-missing-options.ts b/src/utils/prompt-for-missing-options.ts index 18cee780d..7c5242dfa 100644 --- a/src/utils/prompt-for-missing-options.ts +++ b/src/utils/prompt-for-missing-options.ts @@ -1,6 +1,8 @@ import { Options, RawOptions, SolidityFrameworkChoices } from "../types"; import inquirer from "inquirer"; import { SOLIDITY_FRAMEWORKS } from "./consts"; +import { validateNpmName } from "./validate-name"; +import { basename, resolve } from "path"; // default values for unspecified args const defaultOptions: RawOptions = { @@ -23,7 +25,13 @@ export async function promptForMissingOptions( name: "project", message: "Your project name:", default: defaultOptions.project, - validate: (value: string) => value.length > 0, + validate: (name: string) => { + const validation = validateNpmName(basename(resolve(name))); + if (validation.valid) { + return true; + } + return "Project " + validation.problems[0]; + }, }, { type: "list", diff --git a/src/utils/validate-name.ts b/src/utils/validate-name.ts new file mode 100644 index 000000000..d88ed40dd --- /dev/null +++ b/src/utils/validate-name.ts @@ -0,0 +1,22 @@ +import validateProjectName from "validate-npm-package-name"; + +type ValidateNpmNameResult = + | { + valid: true; + } + | { + valid: false; + problems: string[]; + }; + +export function validateNpmName(name: string): ValidateNpmNameResult { + const nameValidation = validateProjectName(name); + if (nameValidation.validForNewPackages) { + return { valid: true }; + } + + return { + valid: false, + problems: [...(nameValidation.errors || []), ...(nameValidation.warnings || [])], + }; +} diff --git a/yarn.lock b/yarn.lock index 6155ceba7..5f7eadad7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -668,6 +668,13 @@ __metadata: languageName: node linkType: hard +"@types/validate-npm-package-name@npm:4.0.2": + version: 4.0.2 + resolution: "@types/validate-npm-package-name@npm:4.0.2" + checksum: 3f35a3cc8ddd919b456843f36d55a4f1df5f03d5d9b6494b4d8f5f3b24e3f24a11c922772d9970a67f1249214da18c157776e9c6d2e72227799459849dfd9c76 + languageName: node + linkType: hard + "@typescript-eslint/eslint-plugin@npm:8.15.0": version: 8.15.0 resolution: "@typescript-eslint/eslint-plugin@npm:8.15.0" @@ -1389,6 +1396,7 @@ __metadata: "@types/inquirer": 9.0.3 "@types/ncp": 2.0.5 "@types/node": 18.16.0 + "@types/validate-npm-package-name": 4.0.2 arg: 5.0.2 chalk: 5.2.0 eslint: ^9.15.0 @@ -1406,6 +1414,7 @@ __metadata: tslib: 2.5.0 typescript: ^5.6.3 typescript-eslint: ^8.15.0 + validate-npm-package-name: 6.0.0 bin: create-eth: bin/create-dapp-se2.js languageName: unknown @@ -5026,6 +5035,13 @@ __metadata: languageName: node linkType: hard +"validate-npm-package-name@npm:6.0.0": + version: 6.0.0 + resolution: "validate-npm-package-name@npm:6.0.0" + checksum: 4d018c4fa07f95534a5fea667adc653b1ef52f08bf56aff066c28394499d0a6949c0b00edbd7077c4dc1e041da9220af7c742ced67d7d2d6a1b07d10cbe91b29 + languageName: node + linkType: hard + "wcwidth@npm:^1.0.1": version: 1.0.1 resolution: "wcwidth@npm:1.0.1"