From facffb548d80ed17dc777c81b8705c4ac608e9b0 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Mon, 13 Jan 2025 17:07:18 -0500 Subject: [PATCH 01/22] feat: fully onboard to create --- .DS_Store | Bin 0 -> 6148 bytes .github/DEVELOPMENT.md | 163 ---- README.md | 5 +- bin/index.js | 16 +- docs/{Tooling.md => Blocks.md} | 92 +- docs/Creation.md | 8 +- docs/FAQs.md | 29 +- docs/Initialization.md | 17 +- docs/Migration.md | 16 +- docs/Options.md | 161 +--- package.json | 18 +- pnpm-lock.yaml | 179 +--- script/__snapshots__/migrate-test-e2e.ts.snap | 273 ------ script/create-test-e2e.ts | 44 - script/initialize-test-e2e.ts | 61 -- script/migrate-test-e2e.ts | 151 ---- script/vitest.config.ts | 3 - src/base.test.ts | 53 ++ src/{next => }/base.ts | 22 +- src/bin/help.test.ts | 354 -------- src/bin/help.ts | 148 --- src/bin/index.test.ts | 205 ----- src/bin/index.ts | 108 --- src/bin/packageJson.test.ts | 31 - src/bin/packageJson.ts | 16 - src/bin/promptForMode.test.ts | 129 --- src/bin/promptForMode.ts | 127 --- .../blocks/blockAllContributors.test.ts | 0 src/{next => }/blocks/blockAllContributors.ts | 2 +- .../blocks/blockAreTheTypesWrong.test.ts | 0 .../blocks/blockAreTheTypesWrong.ts | 2 +- src/{next => }/blocks/blockCSpell.test.ts | 0 src/{next => }/blocks/blockCSpell.ts | 2 +- .../blocks/blockContributingDocs.test.ts | 0 .../blocks/blockContributingDocs.ts | 0 .../blocks/blockContributorCovenant.test.ts | 0 .../blocks/blockContributorCovenant.ts | 0 .../blocks/blockDevelopmentDocs.test.ts | 0 src/{next => }/blocks/blockDevelopmentDocs.ts | 0 src/{next => }/blocks/blockESLint.test.ts | 6 +- src/{next => }/blocks/blockESLint.ts | 2 +- src/{next => }/blocks/blockESLintComments.ts | 0 src/{next => }/blocks/blockESLintJSDoc.ts | 0 src/{next => }/blocks/blockESLintJSONC.ts | 0 src/{next => }/blocks/blockESLintMarkdown.ts | 0 .../blocks/blockESLintMoreStyling.ts | 0 src/{next => }/blocks/blockESLintNode.ts | 0 .../blocks/blockESLintPackageJson.ts | 0 .../blocks/blockESLintPerfectionist.ts | 0 src/{next => }/blocks/blockESLintRegexp.ts | 0 src/{next => }/blocks/blockESLintYML.ts | 0 .../blocks/blockExampleFiles.test.ts | 0 src/{next => }/blocks/blockExampleFiles.ts | 0 src/{next => }/blocks/blockFunding.ts | 2 +- .../blocks/blockGitHubActionsCI.test.ts | 10 +- src/{next => }/blocks/blockGitHubActionsCI.ts | 4 +- src/{next => }/blocks/blockGitHubApps.ts | 0 .../blocks/blockGitHubIssueTemplates.ts | 2 +- .../blocks/blockGitHubPRTemplate.ts | 0 src/{next => }/blocks/blockGitignore.test.ts | 0 src/{next => }/blocks/blockGitignore.ts | 2 +- src/{next => }/blocks/blockKnip.ts | 5 +- src/{next => }/blocks/blockMITLicense.ts | 0 .../blocks/blockMarkdownlint.test.ts | 0 src/{next => }/blocks/blockMarkdownlint.ts | 2 +- src/{next => }/blocks/blockNvmrc.test.ts | 0 src/{next => }/blocks/blockNvmrc.ts | 0 src/{next => }/blocks/blockPRCompliance.ts | 2 +- .../blocks/blockPackageJson.test.ts | 0 src/{next => }/blocks/blockPackageJson.ts | 0 src/{next => }/blocks/blockPnpmDedupe.ts | 0 src/{next => }/blocks/blockPrettier.test.ts | 6 +- src/{next => }/blocks/blockPrettier.ts | 2 +- .../blocks/blockPrettierPluginCurly.ts | 0 .../blocks/blockPrettierPluginPackageJson.ts | 0 .../blocks/blockPrettierPluginSh.ts | 0 src/{next => }/blocks/blockREADME.test.ts | 0 src/{next => }/blocks/blockREADME.ts | 0 src/{next => }/blocks/blockReleaseIt.ts | 4 +- src/{next => }/blocks/blockRenovate.ts | 0 .../blockRepositoryBranchRuleset.test.ts | 0 .../blocks/blockRepositoryBranchRuleset.ts | 0 .../blocks/blockRepositoryLabels.ts | 2 +- .../blocks/blockRepositorySecrets.ts | 0 .../blocks/blockRepositorySettings.ts | 0 src/{next => }/blocks/blockSecurityDocs.ts | 0 src/{next => }/blocks/blockTSup.test.ts | 0 src/{next => }/blocks/blockTSup.ts | 2 +- .../blockTemplatedWith.ts} | 2 +- src/{next => }/blocks/blockTypeScript.test.ts | 0 src/{next => }/blocks/blockTypeScript.ts | 2 +- src/{next => }/blocks/blockVSCode.test.ts | 0 src/{next => }/blocks/blockVSCode.ts | 0 src/{next => }/blocks/blockVitest.test.ts | 0 src/{next => }/blocks/blockVitest.ts | 2 +- .../files}/createMultiWorkflowFile.ts | 0 .../files}/createSoloWorkflowFile.ts | 0 .../files}/formatIgnoreFile.ts | 0 .../files}/formatWorkflowYaml.ts | 2 +- .../formatters => blocks/files}/formatYaml.ts | 4 +- src/blocks/index.ts | 130 +++ src/{next => }/blocks/options.fakes.ts | 0 .../outcomeLabels.ts | 2 +- src/{next => }/blocks/phases.ts | 0 src/create/createAndEnterGitDirectory.test.ts | 82 -- src/create/createAndEnterGitDirectory.ts | 26 - .../createRerunDirectorySuggestion.test.ts | 48 - src/create/createRerunDirectorySuggestion.ts | 7 - src/create/createRerunSuggestion.test.ts | 185 ---- src/create/createRerunSuggestion.ts | 81 -- src/create/createWithOptions.test.ts | 195 ---- src/create/createWithOptions.ts | 81 -- src/create/index.test.ts | 72 -- src/create/index.ts | 74 -- src/{next/blocks => data}/packageData.test.ts | 2 +- src/{next/blocks => data}/packageData.ts | 10 +- src/docs.test.ts | 74 ++ src/greet.test.ts | 44 - src/greet.ts | 13 - src/index.test.ts | 16 + src/index.ts | 13 +- src/initialize/index.test.ts | 108 --- src/initialize/index.ts | 44 - src/initialize/initializeWithOptions.test.ts | 188 ---- src/initialize/initializeWithOptions.ts | 68 -- src/migrate/index.test.ts | 107 --- src/migrate/index.ts | 44 - src/migrate/migrateWithOptions.test.ts | 199 ----- src/migrate/migrateWithOptions.ts | 74 -- src/next/blocks/index.ts | 42 - src/next/blocks/sourcePackageJson.ts | 7 - src/next/presets/index.ts | 3 - src/next/runCreateEnginePreset.ts | 23 - .../getUsageFromReadme.test.ts | 0 .../getUsageFromReadme.ts | 0 .../parsePackageAuthor.test.ts | 10 +- .../parsePackageAuthor.ts | 2 +- .../readDefaultsFromReadme.test.ts | 4 +- .../readDefaultsFromReadme.ts | 9 +- src/{next => options}/readDescription.test.ts | 16 +- src/{next => options}/readDescription.ts | 6 +- .../readDescriptionFromReadme.test.ts | 0 .../readDescriptionFromReadme.ts | 0 .../readDocumentation.test.ts | 0 src/{next => options}/readDocumentation.ts | 2 +- .../readEmails.ts | 2 +- .../readFileAsJson.test.ts | 0 src/{shared => options}/readFileAsJson.ts | 0 src/{shared => options}/readFileSafe.test.ts | 0 src/{shared => options}/readFileSafe.ts | 0 .../readFunding.ts | 2 +- .../readGitHubEmail.test.ts | 2 +- .../readGitHubEmail.ts | 2 +- .../readGuide.test.ts | 2 +- .../readGuide.ts | 2 +- .../options/readLogoSizing.test.ts | 0 src/{shared => }/options/readLogoSizing.ts | 0 src/options/readPackageData.ts | 10 + .../presetCommon.ts => presets/common.ts} | 2 +- .../everything.ts} | 2 +- src/presets/index.ts | 13 + .../presetMinimal.ts => presets/minimal.ts} | 4 +- .../generateNextSteps.test.ts.snap | 183 ---- src/shared/cli/lines.ts | 14 - src/shared/cli/lowerFirst.ts | 3 - src/shared/cli/outro.test.ts | 61 -- src/shared/cli/outro.ts | 28 - src/shared/cli/spinners.ts | 72 -- src/shared/cli/startLineWithDots.ts | 38 - src/shared/codes.ts | 7 - src/shared/createCleanupCommands.test.ts | 32 - src/shared/createCleanupCommands.ts | 12 - src/shared/doesRepositoryExist.test.ts | 47 - src/shared/doesRepositoryExist.ts | 27 - src/shared/ensureGitRepository.test.ts | 29 - src/shared/ensureGitRepository.ts | 9 - src/shared/generateNextSteps.test.ts | 50 -- src/shared/generateNextSteps.ts | 69 -- .../getGitHubUserAsAllContributor.test.ts | 103 --- src/shared/getGitHubUserAsAllContributor.ts | 50 -- src/shared/isUsingCreateEngine.ts | 5 - src/shared/options/args.ts | 332 ------- .../augmentOptionsWithExcludes.test.ts | 189 ---- .../options/augmentOptionsWithExcludes.ts | 108 --- .../createOptionDefaults/index.test.ts | 218 ----- .../options/createOptionDefaults/index.ts | 61 -- .../options/createRepositoryWithApi.test.ts | 71 -- src/shared/options/createRepositoryWithApi.ts | 41 - .../options/detectEmailRedundancy.test.ts | 38 - src/shared/options/detectEmailRedundancy.ts | 23 - .../options/ensureRepositoryExists.test.ts | 170 ---- src/shared/options/ensureRepositoryExists.ts | 95 -- src/shared/options/exclusionKeys.ts | 158 ---- src/shared/options/getBase.test.ts | 59 -- src/shared/options/getBase.ts | 34 - .../getPrefillOrPromptedOption.test.ts | 106 --- .../options/getPrefillOrPromptedOption.ts | 48 - src/shared/options/logInferredOptions.test.ts | 118 --- src/shared/options/logInferredOptions.ts | 39 - src/shared/options/optionsSchema.ts | 66 -- src/shared/options/readOptions.test.ts | 843 ------------------ src/shared/options/readOptions.ts | 305 ------- src/shared/packages.test.ts | 66 -- src/shared/packages.ts | 26 - src/shared/prompts.ts | 5 - src/shared/readFileSafeAsJson.ts | 5 - src/shared/runOrRestore.test.ts | 95 -- src/shared/runOrRestore.ts | 43 - src/shared/types.ts | 127 --- src/steps/addOwnerAsAllContributor.test.ts | 104 --- src/steps/addOwnerAsAllContributor.ts | 61 -- src/steps/addToolAllContributors.test.ts | 39 - src/steps/addToolAllContributors.ts | 16 - src/steps/clearChangelog.ts | 9 - src/steps/clearLocalGitTags.ts | 7 - src/steps/clearUnnecessaryFiles.ts | 37 - .../createJoshuaKGoldbergReplacement.test.ts | 26 - src/steps/createJoshuaKGoldbergReplacement.ts | 21 - src/steps/finalizeDependencies.test.ts | 103 --- src/steps/finalizeDependencies.ts | 75 -- ...initializeBranchProtectionSettings.test.ts | 222 ----- src/steps/initializeGitHubRepository/index.ts | 22 - .../initializeBranchProtectionSettings.ts | 76 -- src/steps/initializeGitRemote.test.ts | 68 -- src/steps/initializeGitRemote.ts | 17 - src/steps/initializeRepositorySettings.ts | 35 - src/steps/populateCSpellDictionary.test.ts | 121 --- src/steps/populateCSpellDictionary.ts | 42 - src/steps/removeSetupScripts.ts | 29 - src/steps/resetGitTags.ts | 13 - src/steps/runCleanup.test.ts | 71 -- src/steps/runCleanup.ts | 43 - src/steps/uninstallPackages.test.ts | 50 -- src/steps/uninstallPackages.ts | 65 -- src/steps/updateAllContributorsTable.ts | 24 - src/steps/updateLocalFiles.test.ts | 550 ------------ src/steps/updateLocalFiles.ts | 94 -- src/steps/updateReadme.test.ts | 138 --- src/steps/updateReadme.ts | 34 - .../writeReadme/findExistingBadges.test.ts | 111 --- src/steps/writeReadme/findExistingBadges.ts | 30 - .../writeReadme/findIntroSectionClose.test.ts | 65 -- .../writeReadme/findIntroSectionClose.ts | 29 - .../writeReadme/generateTopContent.test.ts | 147 --- src/steps/writeReadme/generateTopContent.ts | 84 -- src/steps/writeReadme/index.test.ts | 319 ------- src/steps/writeReadme/index.ts | 73 -- .../creation/createCSpellConfig.test.ts | 58 -- .../writing/creation/createCSpellConfig.ts | 38 - .../creation/createDotGitignore.test.ts | 26 - .../writing/creation/createDotGitignore.ts | 12 - .../creation/createESLintConfig.test.ts | 172 ---- .../writing/creation/createESLintConfig.ts | 138 --- .../writing/creation/createKnipConfig.test.ts | 66 -- .../writing/creation/createKnipConfig.ts | 19 - .../writing/creation/createTsupConfig.test.ts | 43 - .../writing/creation/createTsupConfig.ts | 17 - .../creation/dotGitHub/actions.test.ts | 31 - .../writing/creation/dotGitHub/actions.ts | 31 - .../dotGitHub/createDevelopment/index.test.ts | 403 --------- .../dotGitHub/createDevelopment/index.ts | 177 ---- .../splitIntoSections.test.ts | 40 - .../createDevelopment/splitIntoSections.ts | 27 - .../dotGitHub/createDotGitHubFiles.test.ts | 600 ------------- .../dotGitHub/createDotGitHubFiles.ts | 292 ------ .../dotGitHub/createMultiWorkflowFile.test.ts | 48 - .../dotGitHub/createSoloWorkflowFile.test.ts | 25 - .../dotGitHub/createWorkflows.test.ts | 378 -------- .../creation/dotGitHub/createWorkflows.ts | 247 ----- src/steps/writing/creation/dotGitHub/index.ts | 14 - .../creation/dotGitHub/issueTemplate.ts | 206 ----- src/steps/writing/creation/dotHusky.ts | 8 - src/steps/writing/creation/dotVSCode.test.ts | 290 ------ src/steps/writing/creation/dotVSCode.ts | 85 -- .../creation/formatters/formatJson.test.ts | 14 - .../writing/creation/formatters/formatJson.ts | 15 - .../creation/formatters/formatTypeScript.ts | 5 - src/steps/writing/creation/index.test.ts | 311 ------- src/steps/writing/creation/index.ts | 99 -- src/steps/writing/creation/rootFiles.ts | 149 ---- src/steps/writing/creation/src.ts | 88 -- .../creation/writeAllContributorsRC.ts | 18 - .../writing/creation/writePackageJson.test.ts | 184 ---- .../writing/creation/writePackageJson.ts | 137 --- src/steps/writing/types.ts | 3 - src/steps/writing/writeStructure.test.ts | 46 - src/steps/writing/writeStructure.ts | 26 - .../writing/writeStructureWorker.test.ts | 55 -- src/steps/writing/writeStructureWorker.ts | 55 -- src/{next => }/template.ts | 6 +- src/types.ts | 28 +- src/{next => }/utils/resolveBin.ts | 0 src/{next => }/utils/sortObject.ts | 0 src/{next => }/utils/swallowError.ts | 0 src/{shared => utils}/tryCatchAsync.ts | 0 .../tryCatchLazyValueAsync.test.ts | 0 .../tryCatchLazyValueAsync.ts | 0 tsconfig.json | 2 +- 298 files changed, 537 insertions(+), 16247 deletions(-) create mode 100644 .DS_Store rename docs/{Tooling.md => Blocks.md} (66%) delete mode 100644 script/__snapshots__/migrate-test-e2e.ts.snap delete mode 100644 script/create-test-e2e.ts delete mode 100644 script/initialize-test-e2e.ts delete mode 100644 script/migrate-test-e2e.ts delete mode 100644 script/vitest.config.ts create mode 100644 src/base.test.ts rename src/{next => }/base.ts (88%) delete mode 100644 src/bin/help.test.ts delete mode 100644 src/bin/help.ts delete mode 100644 src/bin/index.test.ts delete mode 100644 src/bin/index.ts delete mode 100644 src/bin/packageJson.test.ts delete mode 100644 src/bin/packageJson.ts delete mode 100644 src/bin/promptForMode.test.ts delete mode 100644 src/bin/promptForMode.ts rename src/{next => }/blocks/blockAllContributors.test.ts (100%) rename src/{next => }/blocks/blockAllContributors.ts (94%) rename src/{next => }/blocks/blockAreTheTypesWrong.test.ts (100%) rename src/{next => }/blocks/blockAreTheTypesWrong.ts (93%) rename src/{next => }/blocks/blockCSpell.test.ts (100%) rename src/{next => }/blocks/blockCSpell.ts (97%) rename src/{next => }/blocks/blockContributingDocs.test.ts (100%) rename src/{next => }/blocks/blockContributingDocs.ts (100%) rename src/{next => }/blocks/blockContributorCovenant.test.ts (100%) rename src/{next => }/blocks/blockContributorCovenant.ts (100%) rename src/{next => }/blocks/blockDevelopmentDocs.test.ts (100%) rename src/{next => }/blocks/blockDevelopmentDocs.ts (100%) rename src/{next => }/blocks/blockESLint.test.ts (98%) rename src/{next => }/blocks/blockESLint.ts (99%) rename src/{next => }/blocks/blockESLintComments.ts (100%) rename src/{next => }/blocks/blockESLintJSDoc.ts (100%) rename src/{next => }/blocks/blockESLintJSONC.ts (100%) rename src/{next => }/blocks/blockESLintMarkdown.ts (100%) rename src/{next => }/blocks/blockESLintMoreStyling.ts (100%) rename src/{next => }/blocks/blockESLintNode.ts (100%) rename src/{next => }/blocks/blockESLintPackageJson.ts (100%) rename src/{next => }/blocks/blockESLintPerfectionist.ts (100%) rename src/{next => }/blocks/blockESLintRegexp.ts (100%) rename src/{next => }/blocks/blockESLintYML.ts (100%) rename src/{next => }/blocks/blockExampleFiles.test.ts (100%) rename src/{next => }/blocks/blockExampleFiles.ts (100%) rename src/{next => }/blocks/blockFunding.ts (77%) rename src/{next => }/blocks/blockGitHubActionsCI.test.ts (96%) rename src/{next => }/blocks/blockGitHubActionsCI.ts (93%) rename src/{next => }/blocks/blockGitHubApps.ts (100%) rename src/{next => }/blocks/blockGitHubIssueTemplates.ts (98%) rename src/{next => }/blocks/blockGitHubPRTemplate.ts (100%) rename src/{next => }/blocks/blockGitignore.test.ts (100%) rename src/{next => }/blocks/blockGitignore.ts (79%) rename src/{next => }/blocks/blockKnip.ts (93%) rename src/{next => }/blocks/blockMITLicense.ts (100%) rename src/{next => }/blocks/blockMarkdownlint.test.ts (100%) rename src/{next => }/blocks/blockMarkdownlint.ts (96%) rename src/{next => }/blocks/blockNvmrc.test.ts (100%) rename src/{next => }/blocks/blockNvmrc.ts (100%) rename src/{next => }/blocks/blockPRCompliance.ts (89%) rename src/{next => }/blocks/blockPackageJson.test.ts (100%) rename src/{next => }/blocks/blockPackageJson.ts (100%) rename src/{next => }/blocks/blockPnpmDedupe.ts (100%) rename src/{next => }/blocks/blockPrettier.test.ts (98%) rename src/{next => }/blocks/blockPrettier.ts (97%) rename src/{next => }/blocks/blockPrettierPluginCurly.ts (100%) rename src/{next => }/blocks/blockPrettierPluginPackageJson.ts (100%) rename src/{next => }/blocks/blockPrettierPluginSh.ts (100%) rename src/{next => }/blocks/blockREADME.test.ts (100%) rename src/{next => }/blocks/blockREADME.ts (100%) rename src/{next => }/blocks/blockReleaseIt.ts (95%) rename src/{next => }/blocks/blockRenovate.ts (100%) rename src/{next => }/blocks/blockRepositoryBranchRuleset.test.ts (100%) rename src/{next => }/blocks/blockRepositoryBranchRuleset.ts (100%) rename src/{next => }/blocks/blockRepositoryLabels.ts (84%) rename src/{next => }/blocks/blockRepositorySecrets.ts (100%) rename src/{next => }/blocks/blockRepositorySettings.ts (100%) rename src/{next => }/blocks/blockSecurityDocs.ts (100%) rename src/{next => }/blocks/blockTSup.test.ts (100%) rename src/{next => }/blocks/blockTSup.ts (96%) rename src/{next/blocks/blockTemplatedBy.ts => blocks/blockTemplatedWith.ts} (91%) rename src/{next => }/blocks/blockTypeScript.test.ts (100%) rename src/{next => }/blocks/blockTypeScript.ts (98%) rename src/{next => }/blocks/blockVSCode.test.ts (100%) rename src/{next => }/blocks/blockVSCode.ts (100%) rename src/{next => }/blocks/blockVitest.test.ts (100%) rename src/{next => }/blocks/blockVitest.ts (98%) rename src/{steps/writing/creation/dotGitHub => blocks/files}/createMultiWorkflowFile.ts (100%) rename src/{steps/writing/creation/dotGitHub => blocks/files}/createSoloWorkflowFile.ts (100%) rename src/{steps/writing/creation/formatters => blocks/files}/formatIgnoreFile.ts (100%) rename src/{steps/writing/creation/dotGitHub => blocks/files}/formatWorkflowYaml.ts (82%) rename src/{steps/writing/creation/formatters => blocks/files}/formatYaml.ts (78%) create mode 100644 src/blocks/index.ts rename src/{next => }/blocks/options.fakes.ts (100%) rename src/{steps/initializeGitHubRepository => blocks}/outcomeLabels.ts (98%) rename src/{next => }/blocks/phases.ts (100%) delete mode 100644 src/create/createAndEnterGitDirectory.test.ts delete mode 100644 src/create/createAndEnterGitDirectory.ts delete mode 100644 src/create/createRerunDirectorySuggestion.test.ts delete mode 100644 src/create/createRerunDirectorySuggestion.ts delete mode 100644 src/create/createRerunSuggestion.test.ts delete mode 100644 src/create/createRerunSuggestion.ts delete mode 100644 src/create/createWithOptions.test.ts delete mode 100644 src/create/createWithOptions.ts delete mode 100644 src/create/index.test.ts delete mode 100644 src/create/index.ts rename src/{next/blocks => data}/packageData.test.ts (95%) rename src/{next/blocks => data}/packageData.ts (68%) create mode 100644 src/docs.test.ts delete mode 100644 src/greet.test.ts delete mode 100644 src/greet.ts create mode 100644 src/index.test.ts delete mode 100644 src/initialize/index.test.ts delete mode 100644 src/initialize/index.ts delete mode 100644 src/initialize/initializeWithOptions.test.ts delete mode 100644 src/initialize/initializeWithOptions.ts delete mode 100644 src/migrate/index.test.ts delete mode 100644 src/migrate/index.ts delete mode 100644 src/migrate/migrateWithOptions.test.ts delete mode 100644 src/migrate/migrateWithOptions.ts delete mode 100644 src/next/blocks/index.ts delete mode 100644 src/next/blocks/sourcePackageJson.ts delete mode 100644 src/next/presets/index.ts delete mode 100644 src/next/runCreateEnginePreset.ts rename src/{shared/options/createOptionDefaults => options}/getUsageFromReadme.test.ts (100%) rename src/{shared/options/createOptionDefaults => options}/getUsageFromReadme.ts (100%) rename src/{shared/options/createOptionDefaults => options}/parsePackageAuthor.test.ts (75%) rename src/{shared/options/createOptionDefaults => options}/parsePackageAuthor.ts (91%) rename src/{shared/options/createOptionDefaults => options}/readDefaultsFromReadme.test.ts (98%) rename src/{shared/options/createOptionDefaults => options}/readDefaultsFromReadme.ts (85%) rename src/{next => options}/readDescription.test.ts (79%) rename src/{next => options}/readDescription.ts (74%) rename src/{next => options}/readDescriptionFromReadme.test.ts (100%) rename src/{next => options}/readDescriptionFromReadme.ts (100%) rename src/{next => options}/readDocumentation.test.ts (100%) rename src/{next => options}/readDocumentation.ts (92%) rename src/{shared/options/createOptionDefaults => options}/readEmails.ts (91%) rename src/{shared => options}/readFileAsJson.test.ts (100%) rename src/{shared => options}/readFileAsJson.ts (100%) rename src/{shared => options}/readFileSafe.test.ts (100%) rename src/{shared => options}/readFileSafe.ts (100%) rename src/{shared/options/createOptionDefaults => options}/readFunding.ts (77%) rename src/{shared/options/createOptionDefaults => options}/readGitHubEmail.test.ts (96%) rename src/{shared/options/createOptionDefaults => options}/readGitHubEmail.ts (88%) rename src/{shared/options/createOptionDefaults => options}/readGuide.test.ts (96%) rename src/{shared/options/createOptionDefaults => options}/readGuide.ts (84%) rename src/{shared => }/options/readLogoSizing.test.ts (100%) rename src/{shared => }/options/readLogoSizing.ts (100%) create mode 100644 src/options/readPackageData.ts rename src/{next/presets/presetCommon.ts => presets/common.ts} (90%) rename src/{next/presets/presetEverything.ts => presets/everything.ts} (97%) create mode 100644 src/presets/index.ts rename src/{next/presets/presetMinimal.ts => presets/minimal.ts} (95%) delete mode 100644 src/shared/__snapshots__/generateNextSteps.test.ts.snap delete mode 100644 src/shared/cli/lines.ts delete mode 100644 src/shared/cli/lowerFirst.ts delete mode 100644 src/shared/cli/outro.test.ts delete mode 100644 src/shared/cli/outro.ts delete mode 100644 src/shared/cli/spinners.ts delete mode 100644 src/shared/cli/startLineWithDots.ts delete mode 100644 src/shared/codes.ts delete mode 100644 src/shared/createCleanupCommands.test.ts delete mode 100644 src/shared/createCleanupCommands.ts delete mode 100644 src/shared/doesRepositoryExist.test.ts delete mode 100644 src/shared/doesRepositoryExist.ts delete mode 100644 src/shared/ensureGitRepository.test.ts delete mode 100644 src/shared/ensureGitRepository.ts delete mode 100644 src/shared/generateNextSteps.test.ts delete mode 100644 src/shared/generateNextSteps.ts delete mode 100644 src/shared/getGitHubUserAsAllContributor.test.ts delete mode 100644 src/shared/getGitHubUserAsAllContributor.ts delete mode 100644 src/shared/isUsingCreateEngine.ts delete mode 100644 src/shared/options/args.ts delete mode 100644 src/shared/options/augmentOptionsWithExcludes.test.ts delete mode 100644 src/shared/options/augmentOptionsWithExcludes.ts delete mode 100644 src/shared/options/createOptionDefaults/index.test.ts delete mode 100644 src/shared/options/createOptionDefaults/index.ts delete mode 100644 src/shared/options/createRepositoryWithApi.test.ts delete mode 100644 src/shared/options/createRepositoryWithApi.ts delete mode 100644 src/shared/options/detectEmailRedundancy.test.ts delete mode 100644 src/shared/options/detectEmailRedundancy.ts delete mode 100644 src/shared/options/ensureRepositoryExists.test.ts delete mode 100644 src/shared/options/ensureRepositoryExists.ts delete mode 100644 src/shared/options/exclusionKeys.ts delete mode 100644 src/shared/options/getBase.test.ts delete mode 100644 src/shared/options/getBase.ts delete mode 100644 src/shared/options/getPrefillOrPromptedOption.test.ts delete mode 100644 src/shared/options/getPrefillOrPromptedOption.ts delete mode 100644 src/shared/options/logInferredOptions.test.ts delete mode 100644 src/shared/options/logInferredOptions.ts delete mode 100644 src/shared/options/optionsSchema.ts delete mode 100644 src/shared/options/readOptions.test.ts delete mode 100644 src/shared/options/readOptions.ts delete mode 100644 src/shared/packages.test.ts delete mode 100644 src/shared/packages.ts delete mode 100644 src/shared/prompts.ts delete mode 100644 src/shared/readFileSafeAsJson.ts delete mode 100644 src/shared/runOrRestore.test.ts delete mode 100644 src/shared/runOrRestore.ts delete mode 100644 src/shared/types.ts delete mode 100644 src/steps/addOwnerAsAllContributor.test.ts delete mode 100644 src/steps/addOwnerAsAllContributor.ts delete mode 100644 src/steps/addToolAllContributors.test.ts delete mode 100644 src/steps/addToolAllContributors.ts delete mode 100644 src/steps/clearChangelog.ts delete mode 100644 src/steps/clearLocalGitTags.ts delete mode 100644 src/steps/clearUnnecessaryFiles.ts delete mode 100644 src/steps/createJoshuaKGoldbergReplacement.test.ts delete mode 100644 src/steps/createJoshuaKGoldbergReplacement.ts delete mode 100644 src/steps/finalizeDependencies.test.ts delete mode 100644 src/steps/finalizeDependencies.ts delete mode 100644 src/steps/initializeBranchProtectionSettings.test.ts delete mode 100644 src/steps/initializeGitHubRepository/index.ts delete mode 100644 src/steps/initializeGitHubRepository/initializeBranchProtectionSettings.ts delete mode 100644 src/steps/initializeGitRemote.test.ts delete mode 100644 src/steps/initializeGitRemote.ts delete mode 100644 src/steps/initializeRepositorySettings.ts delete mode 100644 src/steps/populateCSpellDictionary.test.ts delete mode 100644 src/steps/populateCSpellDictionary.ts delete mode 100644 src/steps/removeSetupScripts.ts delete mode 100644 src/steps/resetGitTags.ts delete mode 100644 src/steps/runCleanup.test.ts delete mode 100644 src/steps/runCleanup.ts delete mode 100644 src/steps/uninstallPackages.test.ts delete mode 100644 src/steps/uninstallPackages.ts delete mode 100644 src/steps/updateAllContributorsTable.ts delete mode 100644 src/steps/updateLocalFiles.test.ts delete mode 100644 src/steps/updateLocalFiles.ts delete mode 100644 src/steps/updateReadme.test.ts delete mode 100644 src/steps/updateReadme.ts delete mode 100644 src/steps/writeReadme/findExistingBadges.test.ts delete mode 100644 src/steps/writeReadme/findExistingBadges.ts delete mode 100644 src/steps/writeReadme/findIntroSectionClose.test.ts delete mode 100644 src/steps/writeReadme/findIntroSectionClose.ts delete mode 100644 src/steps/writeReadme/generateTopContent.test.ts delete mode 100644 src/steps/writeReadme/generateTopContent.ts delete mode 100644 src/steps/writeReadme/index.test.ts delete mode 100644 src/steps/writeReadme/index.ts delete mode 100644 src/steps/writing/creation/createCSpellConfig.test.ts delete mode 100644 src/steps/writing/creation/createCSpellConfig.ts delete mode 100644 src/steps/writing/creation/createDotGitignore.test.ts delete mode 100644 src/steps/writing/creation/createDotGitignore.ts delete mode 100644 src/steps/writing/creation/createESLintConfig.test.ts delete mode 100644 src/steps/writing/creation/createESLintConfig.ts delete mode 100644 src/steps/writing/creation/createKnipConfig.test.ts delete mode 100644 src/steps/writing/creation/createKnipConfig.ts delete mode 100644 src/steps/writing/creation/createTsupConfig.test.ts delete mode 100644 src/steps/writing/creation/createTsupConfig.ts delete mode 100644 src/steps/writing/creation/dotGitHub/actions.test.ts delete mode 100644 src/steps/writing/creation/dotGitHub/actions.ts delete mode 100644 src/steps/writing/creation/dotGitHub/createDevelopment/index.test.ts delete mode 100644 src/steps/writing/creation/dotGitHub/createDevelopment/index.ts delete mode 100644 src/steps/writing/creation/dotGitHub/createDevelopment/splitIntoSections.test.ts delete mode 100644 src/steps/writing/creation/dotGitHub/createDevelopment/splitIntoSections.ts delete mode 100644 src/steps/writing/creation/dotGitHub/createDotGitHubFiles.test.ts delete mode 100644 src/steps/writing/creation/dotGitHub/createDotGitHubFiles.ts delete mode 100644 src/steps/writing/creation/dotGitHub/createMultiWorkflowFile.test.ts delete mode 100644 src/steps/writing/creation/dotGitHub/createSoloWorkflowFile.test.ts delete mode 100644 src/steps/writing/creation/dotGitHub/createWorkflows.test.ts delete mode 100644 src/steps/writing/creation/dotGitHub/createWorkflows.ts delete mode 100644 src/steps/writing/creation/dotGitHub/index.ts delete mode 100644 src/steps/writing/creation/dotGitHub/issueTemplate.ts delete mode 100644 src/steps/writing/creation/dotHusky.ts delete mode 100644 src/steps/writing/creation/dotVSCode.test.ts delete mode 100644 src/steps/writing/creation/dotVSCode.ts delete mode 100644 src/steps/writing/creation/formatters/formatJson.test.ts delete mode 100644 src/steps/writing/creation/formatters/formatJson.ts delete mode 100644 src/steps/writing/creation/formatters/formatTypeScript.ts delete mode 100644 src/steps/writing/creation/index.test.ts delete mode 100644 src/steps/writing/creation/index.ts delete mode 100644 src/steps/writing/creation/rootFiles.ts delete mode 100644 src/steps/writing/creation/src.ts delete mode 100644 src/steps/writing/creation/writeAllContributorsRC.ts delete mode 100644 src/steps/writing/creation/writePackageJson.test.ts delete mode 100644 src/steps/writing/creation/writePackageJson.ts delete mode 100644 src/steps/writing/types.ts delete mode 100644 src/steps/writing/writeStructure.test.ts delete mode 100644 src/steps/writing/writeStructure.ts delete mode 100644 src/steps/writing/writeStructureWorker.test.ts delete mode 100644 src/steps/writing/writeStructureWorker.ts rename src/{next => }/template.ts (56%) rename src/{next => }/utils/resolveBin.ts (100%) rename src/{next => }/utils/sortObject.ts (100%) rename src/{next => }/utils/swallowError.ts (100%) rename src/{shared => utils}/tryCatchAsync.ts (100%) rename src/{shared => utils}/tryCatchLazyValueAsync.test.ts (100%) rename src/{shared => utils}/tryCatchLazyValueAsync.ts (100%) diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..69944ba1a5be222e2807859e027cd7d2fb74ba4c GIT binary patch literal 6148 zcmeHK%}T>S5Z<*_6N-?7LXQhx3)ZTI;w8lT0!H+pQWH}&7_%iw&7l->))(?gd>&_Z zH()Vm5jz9B-~8@oKgj+t#<(|+4jFS8V-_?-j!J`|yD_w3k`XzMkrDf#o){no8W_O+K|n)v4OSY})&U(}pE2G>L;)S&5{SZ}Yp~J?5fH9R0d*-iPYkZh z!7ognYp~L&%NbWQ!#HN<`ticm?BEwFopDzq^~3-%u*yJ7n+~4;=kUukKJr&f$RY-a zfq%vTZ;$=42a7Ui>yPE(Su3F3Lqoy53KbC0S1tiy;6Bn-K^+&UL!N7}(ukv=UzG#W OML-ck9Wn3=4158H$4Q9* literal 0 HcmV?d00001 diff --git a/.github/DEVELOPMENT.md b/.github/DEVELOPMENT.md index 39161bc07..c7187f848 100644 --- a/.github/DEVELOPMENT.md +++ b/.github/DEVELOPMENT.md @@ -104,166 +104,3 @@ Add `--watch` to keep the type checker running in a watch mode that updates the ```shell pnpm tsc --watch ``` - -## Setup Scripts - -As described in the `README.md` file and `docs/`, this template repository comes with three scripts that can set up an existing or new repository. - -Each follows roughly the same general flow: - -1. `bin/index.ts` uses `bin/mode.ts` to determine which of the three setup scripts to run -2. `readOptions` parses in options from local files, Git commands, npm APIs, and/or files on disk -3. `runOrRestore` wraps the setup script's main logic in a friendly prompt wrapper -4. The setup script wraps each portion of its main logic with `withSpinner` - - Each step of setup logic is generally imported from within `src/steps` -5. A call to `outro` summarizes the results for the user - -> **Warning** -> Each setup script overrides many files in the directory they're run in. -> Make sure to save any changes you want to preserve before running them. - -### The Creation Script - -> 📝 See [`docs/Creation.md`](../docs/Creation.md) for user documentation on the creation script. - -This template's "creation" script is located in `src/create/`. -You can run it locally with `node bin/index.js --mode create`. -Note that files need to be built with `pnpm run build` beforehand. - -#### Testing the Creation Script - -You can run the end-to-end test for creation locally on the command-line. -Note that the files need to be built with `pnpm run build` beforehand. - -```shell -pnpm run test:create -``` - -That end-to-end test executes `script/create-test-e2e.ts`, which: - -1. Runs the creation script to create a new `test-repository` child directory and repository, capturing code coverage -2. Asserts that commands such as `build` and `lint` each pass - -The `pnpm run test:create` script is run in CI to ensure that templating changes are in sync with the template's actual files. -See `.github/workflows/ci.yml`'s `test_creation_script` job. - -### The Initialization Script - -> 📝 See [`docs/Initialization.md`](../docs/Initialization.md) for user documentation on the initialization script. - -This template's "initialization" script is located in `src/initialize/`. -You can run it locally with `pnpm run initialize`. -It uses [`tsx`](https://github.com/esbuild-kit/tsx) so you don't need to build files before running. - -```shell -pnpm run initialize -``` - -#### Testing the Initialization Script - -You can run the end-to-end test for initializing locally on the command-line. -Note that files need to be built with `pnpm run build` beforehand. - -```shell -pnpm run test:initialize -``` - -That end-to-end test executes `script/initialize-test-e2e.ts`, which: - -1. Runs the initialization script using `--skip-github-api` and other skip flags -2. Checks that the local repository's files were changed correctly (e.g. removed initialization-only files) -3. Runs `pnpm run lint:knip` to make sure no excess dependencies or files were left over -4. Resets everything -5. Runs initialization a second time, capturing test coverage - -The `pnpm run test:initialize` script is run in CI to ensure that templating changes are in sync with the template's actual files. -See `.github/workflows/ci.yml`'s `test_initialization_script` job. - -### The Migration Script - -> 📝 See [`docs/Migration.md`](../docs/Migration.md) for user documentation on the migration script. - -This template's "migration" script is located in `src/migrate/`. -Note that files need to be built with `pnpm run build` beforehand. - -To test out the script locally, run it from a different repository's directory: - -```shell -cd ../other-repo -node ../create-typescript-app/bin/migrate.js -``` - -The migration script will work on any directory. -You can try it out in a blank directory with scripts like: - -```shell -cd .. -mkdir temp -cd temp -node ../create-typescript-app/bin/migrate.js -``` - -#### Testing the Migration Script - -> 💡 Seeing `Oh no! Running the migrate script unexpectedly modified:` errors? -> _[Unexpected File Modifications](#unexpected-file-modifications)_ covers that below. - -You can run the end-to-end test for migrating locally on the command-line: - -```shell -pnpm run test:migrate -``` - -That end-to-end test executes `script/migrate-test-e2e.ts`, which: - -1. Runs the migration script using `--skip-github-api` and other skip flags, capturing code coverage -2. Checks that only a small list of allowed files were changed -3. Checks that the local repository's files were changed correctly (e.g. removed initialization-only files) - -The `pnpm run test:migrate` script is run in CI to ensure that templating changes are in sync with the template's actual files. -See `.github/workflows/ci.yml`'s `test_migration_script` job. - -> Tip: if the migration test is failing in CI and you don't see any errors, try [downloading the full logs](https://docs.github.com/en/actions/monitoring-and-troubleshooting-workflows/using-workflow-run-logs#downloading-logs). - -##### Migration Snapshot Failures - -The migration test uses the [Vitest file snapshot](https://vitest.dev/guide/snapshot#file-snapshots) in `script/__snapshots__/migrate-test-e2e.ts.snap` to store expected differences to this repository after running the migration script. -The end-to-end migration test will fail any changes that don't keep the same differences in that snapshot. - -You can update the snapshot file by: - -1. Committing any changes to your local repository -2. Running `pnpm i` and `pnpm build` if any updates have been made to the `package.json` or `src/` files, respectively -3. Running `pnpm run test:migrate -u` to update the snapshot - -At this point there will be some files changed: - -- `script/__snapshots__/migrate-test-e2e.ts.snap` will have updates if any files mismatched templates -- The actual updated files on disk will be there too - -If the snapshot file changes are what you expected, then you can commit them. -The rest of the file changes can be reverted. - -> [🚀 Feature: Add a way to apply known file changes after migration #1184](https://github.com/JoshuaKGoldberg/create-typescript-app/issues/1184) tracks turning the test snapshot into a feature. - -##### Unexpected File Modifications - -The migration test also asserts that no files were unexpectedly changed. -If you see a failure like: - -```plaintext -Oh no! Running the migrate script unexpectedly modified: - - ... -``` - -...then that means the file generated from templates differs from what's checked into the repository. -This is most often caused by changes to templates not being applied to checked-in files too. - -Templates for files are generally stored in [`src/steps/writing/creation`] under a path roughly corresponding to the file they describe. -For example, the template for `tsup.config.ts` is stored in [`src/steps/writing/creation/createTsupConfig.ts`](../src/steps/writing/creation/createTsupConfig.ts). -If the `createTsupConfig` function were to be modified without an equivalent change to `tsup.config.ts` -or vice-versa- then the migration test would report: - -```plaintext -Oh no! Running the migrate script unexpectedly modified: - - tsup.config.ts -``` diff --git a/README.md b/README.md index efc7f1e78..a5894ebbe 100644 --- a/README.md +++ b/README.md @@ -31,10 +31,11 @@ First make sure you have the following installed: Then in an existing repository or in your directory where you'd like to make a new repository: ```shell -npx create-typescript-app +npx create typescript-app ``` -That setup script will walk you through using the template. +That will launch `create-typescript-app` using the [`create` runner](https://create.bingo). + You can read more about the supported setup modes in their docs pages: - [**Creating from the terminal**](./docs/Creation.md): creating a new repository locally on the command-line _(recommended)_ diff --git a/bin/index.js b/bin/index.js index 1b7a28434..a2fcfd5e6 100755 --- a/bin/index.js +++ b/bin/index.js @@ -1,4 +1,16 @@ #!/usr/bin/env node -import { bin } from "../lib/bin/index.js"; +import chalk from "chalk"; -process.exitCode = await bin(process.argv.slice(2)); +console.log( + [ + "create-typescript-app is now run using ", + chalk.bold("create"), + ".\n\nRun:\n ", + chalk.bold("npx create", process.argv.slice(2).join(" ")), + "\n\nYou can read more on:\n https://", + chalk.bold("create.bingo"), + "\n\nThanks for using create-typescript-app! 🎁", + ].join(""), +); + +process.exitCode = 1; diff --git a/docs/Tooling.md b/docs/Blocks.md similarity index 66% rename from docs/Tooling.md rename to docs/Blocks.md index 983f8063b..6ff111b38 100644 --- a/docs/Tooling.md +++ b/docs/Blocks.md @@ -1,48 +1,54 @@ -# Tooling +# Blocks `create-typescript-app` provides over two dozen pieces of tooling, ranging from code building and formatting to various forms of GitHub repository management. -Most can be individually turned off or on. - -The `create-typescript-app` setup scripts -[creation](./Creation.md), [initialization](./Initialization.md), and [migration](./Migration.md)- will prompt you to choose a "base" template level to initialize from. -Those template levels provide common presets of which tooling pieces to enable. - -```plaintext -◆ How much tooling would you like the template to set up for you? -│ ○ minimal Just the bare starter tooling most repositories should ideally include. -│ ○ common Important additions to the minimal starters such as releases and tests. -│ ○ everything The most thorough tooling imaginable: sorting, spellchecking, and more! -│ ○ prompt (allow me to customize) -└ -``` - -This table summarizes each tooling piece and which base levels they're included in: - -| Tooling Piece | Exclusion Flag | Minimal | Common | Everything | -| --------------------------------------------- | ------------------------------ | ------- | ------ | ---------- | -| [Building](#building) | | ✔️ | ✅ | 💯 | -| [Compliance](#compliance) | `--exclude-compliance` | | | 💯 | -| [Contributors](#contributors) | `--exclude-contributors` | | ✅ | 💯 | -| [Formatting](#formatting) | | ✔️ | ✅ | 💯 | -| [Lint ESLint](#lint-eslint) | `--exclude-lint-eslint` | | | 💯 | -| [Lint JSDoc](#lint-jsdoc) | `--exclude-lint-jsdoc` | | | 💯 | -| [Lint JSON](#lint-json) | `--exclude-lint-json` | | | 💯 | -| [Lint Knip](#lint-knip) | `--exclude-lint-knip` | | ✅ | 💯 | -| [Lint MD](#lint-md) | `--exclude-lint-md` | | | 💯 | -| [Lint Package JSON](#lint-package-json) | `--exclude-lint-package-json` | | | 💯 | -| [Lint Packages](#lint-packages) | `--exclude-lint-packages` | | | 💯 | -| [Lint Perfectionist](#lint-perfectionist) | `--exclude-lint-perfectionist` | | | 💯 | -| [Lint Regexp](#lint-regexp) | `--exclude-lint-regexp` | | | 💯 | -| [Lint Spelling](#lint-spelling) | `--exclude-lint-spelling` | | | 💯 | -| [Lint Strict](#lint-strict) | `--exclude-lint-strict` | | | 💯 | -| [Lint Stylistic](#lint-stylistic) | `--exclude-lint-stylistic` | | | 💯 | -| [Lint YML](#lint-yml) | `--exclude-lint-yml` | | | 💯 | -| [Linting](#linting) | | ✔️ | ✅ | 💯 | -| [Package Management](#package-management) | | ✔️ | ✅ | 💯 | -| [Releases](#releases) | `--exclude-releases` | | ✅ | 💯 | -| [Renovate](#renovate) | `--exclude-renovate` | | ✅ | 💯 | -| [Repository Templates](#repository-templates) | | ✔️ | ✅ | 💯 | -| [Testing](#testing) | `--exclude-tests` | | ✅ | 💯 | -| [Type Checking](#type-checking) | | ✔️ | ✅ | 💯 | +Each can be individually turned off or on. + +This table summarizes each block and which base levels they're included in: + +| Block | Exclusion Flag | Minimal | Common | Everything | +| ---------------------------- | ---------------------------------------- | ------- | ------ | ---------- | +| AllContributors | `--exclude-allcontributors` | | ✅ | 💯 | +| Are The Types Wrong | `--exclude-are-the-types-wrong` | | | | +| Contributing Docs | `--exclude-contributing-docs` | ✔️ | ✅ | 💯 | +| Contributor Covenant | `--exclude-contributor-covenant` | ✔️ | ✅ | 💯 | +| CSpell | `--exclude-cspell` | | | 💯 | +| Development Docs | `--exclude-development-docs` | ✔️ | ✅ | 💯 | +| ESLint | `--exclude-eslint` | ✔️ | ✅ | 💯 | +| ESLint Comments Plugin | `--exclude-eslint-comments-plugin` | | | 💯 | +| ESLint JSDoc Plugin | `--exclude-eslint-jsdoc-plugin` | | | 💯 | +| ESLint JSONC Plugin | `--exclude-eslint-jsonc-plugin` | | | 💯 | +| ESLint Markdown Plugin | `--exclude-eslint-markdown-plugin` | | | 💯 | +| ESLint More Styling | `--exclude-eslint-more-styling` | | | 💯 | +| ESLint Node Plugin | `--exclude-eslint-node-plugin` | | | 💯 | +| ESLint package.json Plugin | `--exclude-eslint-package-json-plugin` | | | 💯 | +| ESLint Perfectionist Plugin | `--exclude-eslint-perfectionist-plugin` | | | 💯 | +| ESLint Regexp Plugin | `--exclude-eslint-regexp-plugin` | | | 💯 | +| ESLint YML Plugin | `--exclude-eslint-yml-plugin` | | | 💯 | +| Funding | `--exclude-funding` | ✔️ | ✅ | 💯 | +| GitHub Actions CI | `--exclude-github-actions-ci` | ✔️ | ✅ | 💯 | +| GitHub Issue Templates | `--exclude-github-issue-templates` | ✔️ | ✅ | 💯 | +| GitHub Issue Templates | `--exclude-github-issue-templates` | ✔️ | ✅ | 💯 | +| Gitignore | `--exclude-gitignore` | ✔️ | ✅ | 💯 | +| Knip | `--exclude-knip` | | | 💯 | +| Markdownlint | `--exclude-markdownlint` | | | 💯 | +| MIT License | `--exclude-mit-license` | ✔️ | ✅ | 💯 | +| nvmrc | `--exclude-nvmrc` | | | 💯 | +| Package JSON | `--exclude-package-json` | ✔️ | ✅ | 💯 | +| pnpm Dedupe | `--exclude-pnpm-dedupe` | | | 💯 | +| PR Compliance | `--exclude-pr-compliance` | | | 💯 | +| Prettier | `--exclude-prettier` | ✔️ | ✅ | 💯 | +| Prettier Plugin Curly | `--exclude-prettier-plugin-curly` | | | 💯 | +| Prettier Plugin Package JSON | `--exclude-prettier-plugin-package-json` | | | 💯 | +| Prettier Plugin Sh | `--exclude-prettier-plugin-sh` | | | 💯 | +| README.md | `--exclude-readme-md` | ✔️ | ✅ | 💯 | +| release-it | `--exclude-release-it` | | ✅ | 💯 | +| Renovate | `--exclude-renovate` | | | 💯 | +| Security Docs | `--exclude-security-docs` | | | 💯 | +| Templated By Notice | `--exclude-templated-by-notice` | ✔️ | ✅ | 💯 | +| TSup | `--exclude-tsup` | ✔️ | ✅ | 💯 | +| TypeScript | `--exclude-typescript` | ✔️ | ✅ | 💯 | +| Vitest | `--exclude-vitest` | | ✅ | 💯 | +| VS Code | `--exclude-vs-code` | | | 💯 | See also [Options](./Options.md) for how to customize the way template is run. diff --git a/docs/Creation.md b/docs/Creation.md index 5572d9d15..ba1f3ac04 100644 --- a/docs/Creation.md +++ b/docs/Creation.md @@ -1,9 +1,9 @@ # Creating from the Terminal -You can run `npx create-typescript-app` in your terminal to interactively create a new repository in a child directory: +You can run `npx create typescript-app` in your terminal to interactively create a new repository in a child directory: ```shell -npx create-typescript-app +npx create typescript-app ``` The creation script will by default: @@ -31,10 +31,10 @@ Hooray! 🥳 You can explicitly provide some or all of the options the script would prompt for as command-line flags. See [Options.md](./Options.md). -For example, running the creation script and skipping all GitHub-related APIs: +For example, running the creation script and skipping the _"This package was templated with..."_ block: ```shell -npx create-typescript-app --mode create --skip-all-contributors-api --skip-github-api +npx create typescript-app --mode create --exclude-templated-with ``` See [Tooling.md](./Tooling.md) for details on the tooling pieces and which bases they're included in. diff --git a/docs/FAQs.md b/docs/FAQs.md index 71eb53746..b65837f51 100644 --- a/docs/FAQs.md +++ b/docs/FAQs.md @@ -147,43 +147,24 @@ That's reasonable. Unless you know a package needs to support a CJS consumer, please strongly consider keeping it ESM-only (the `create-typescript-app` default). ESM-only packages have a smaller footprint by virtue of including fewer files. -## Is there a way to pull in template updates to previously created repositories? - -Not directly. -You can always copy & paste them in manually, and/or re-run `npx create-typescript-app --mode migrate`. - -See [🚀 Feature: Add a script to sync the tooling updates from forked template repo #498](https://github.com/JoshuaKGoldberg/create-typescript-app/issues/498): it will likely eventually be possible. - ## What about `eslint-config-prettier`? [`eslint-config-prettier`](https://github.com/prettier/eslint-config-prettier) is an ESLint plugin that serves only to turn off all rules that are unnecessary or might conflict with formatters such as Prettier. None of the ESLint configs enabled by this repository's tooling leave any rules enabled that would need to be disabled. Using `eslint-config-prettier` would be redundant. -## What determines which "base" a tool goes into? +## What determines which preset a tool goes into? -The four bases correspond to what have seemed to be the most common user needs of template consumers: +The four presets correspond to what have seemed to be the most common user needs of template consumers: 1. **Minimal**: Developers who just want the barest of starting templates. - They may be very new to TypeScript tooling, or they may have made an informed decision that the additional tooling isn't worth the complexity and/or time investment. - - Tooling in this base is only what would be essential for a small TypeScript package that can be built, formatted, linted, and released. + - Tooling in this preset is only what would be essential for a small TypeScript package that can be built, formatted, linted, and released. 2. **Common**: The common case of users who want the minimal tooling along with common repository management. - - Tooling added in this base should be essential for a TypeScript repository that additionally automates useful GitHub tasks: contributor recognition, release management, and testing. + - Tooling added in this preset should be essential for a TypeScript repository that additionally automates useful GitHub tasks: contributor recognition, release management, and testing. 3. **Everything**: Power users (including this repository) who want as much of the latest and greatest safety checks and standardization as possible. -Note that users can always customize exactly with portions are kept in with `--base` **`prompt`**. - -## Which tools can't I remove? - -The following pieces of this template's tooling don't have options to be removed: - -- Linting with ESLint and `pnpm run lint` -- GitHub repository metadata such as the code of conduct and issue templates -- Prettier and `pnpm run format` -- tsup and `pnpm run build` -- TypeScript and `pnpm run tsc` - -If you have a strong desire to add an `--exclude-*` flag for any of them, please do [file a feature request](https://github.com/JoshuaKGoldberg/create-typescript-app/issues/new?assignees=&labels=type%3A+feature&projects=&template=03-feature.yml&title=%F0%9F%9A%80+Feature%3A+%3Cshort+description+of+the+feature%3E). +Note that you can always customize exactly which preset you use per [Options](./Options.md). ## Why does this package include so many tools? diff --git a/docs/Initialization.md b/docs/Initialization.md index f2f17d3f7..4ec14ead3 100644 --- a/docs/Initialization.md +++ b/docs/Initialization.md @@ -1,21 +1,12 @@ # Initializing from the Template -As an alternative to [creating with `npx create-typescript-app`](./Creation.md), the [_Use this template_](https://github.com/JoshuaKGoldberg/create-typescript-app/generate) button on GitHub can be used to quickly create a new repository from the template. +As an alternative to [creating with `npx create typescript-app`](./Creation.md), the [_Use this template_](https://github.com/JoshuaKGoldberg/create-typescript-app/generate) button on GitHub can be used to quickly create a new repository from the template. You can set up the new repository locally by cloning it and installing packages: ```shell git clone https://github.com/YourUsername/YourRepositoryName cd YourRepositoryName -pnpm i -``` - -> 💡 If you don't want to clone it locally, you can always [develop in a codespace](https://docs.github.com/en/codespaces/developing-in-codespaces/developing-in-a-codespace) instead. - -Once the repository's packages are installed, you can run `pnpm run initialize` to fill out your repository's details and install necessary packages. -It will then remove itself and uninstall dependencies only used for initialization. - -```shell -pnpm run initialize +npx create typescript-app ``` You'll then need to manually go through the following two steps to set up tooling on GitHub: @@ -37,10 +28,10 @@ See [Options.md](./Options.md). `pnpm run initialize` will set `--mode` to `initialize`. -For example, running the initialization script and skipping all GitHub-related APIs: +For example, running the creation script and skipping the _"This package was templated with..."_ block: ```shell -pnpm run initialize --skip-all-contributors-api --skip-github-api +npx create typescript-app --mode create --exclude-templated-with ``` See [Tooling.md](./Tooling.md) for details on the tooling pieces and which bases they're included in. diff --git a/docs/Migration.md b/docs/Migration.md index ef03d0276..3526c53ae 100644 --- a/docs/Migration.md +++ b/docs/Migration.md @@ -1,9 +1,9 @@ # Migrating an Existing Repository -If you have an existing repository that you'd like to give the files from this repository, you can run `npx create-typescript-app` in it to "migrate" its tooling to this template's. +If you have an existing repository that you'd like to give the files from this repository, you can run `npx create typescript-app` in it to "migrate" its tooling to this template's. ```shell -npx create-typescript-app +npx create typescript-app ``` The migration script will: @@ -17,6 +17,7 @@ The migration script will: For example, if the repository previously using Jest for testing: - `eslint-plugin-jest`, `jest`, and other Jest-related packages will be uninstalled + - 🛑 TODO: Missing in `blockVitest` - Any Jest config file like `jest.config.js` will be deleted - `@vitest/eslint-plugin`, `vitest`, and other Vitest-related packages will be installed - A `vitest.config.ts` file will be created @@ -40,20 +41,13 @@ Hooray! 🥳 ## Options -`create-typescript-app` will detect whether it's being run in an existing repository. -It also allows specifying `--mode migrate` if that detection misinterprets the current directory: - -```shell -npx create-typescript-app --mode migrate -``` - You can explicitly provide some or all of the options the script would prompt for as command-line flags. See [Options.md](./Options.md). -For example, running the migration script and skipping all GitHub-related APIs: +For example, running the migration script and skipping the _"This package was templated with..."_ block: ```shell -npx create-typescript-app --skip-all-contributors-api --skip-github-api +npx create typescript-app --mode migrate --exclude-templated-with ``` See [Tooling.md](./Tooling.md) for details on the tooling pieces and which bases they're included in. diff --git a/docs/Options.md b/docs/Options.md index 79fd90faa..8e67bdfe4 100644 --- a/docs/Options.md +++ b/docs/Options.md @@ -1,55 +1,50 @@ # Options -All three of `create-typescript-app`'s setup scripts -[creation](./Creation.md), [initialization](./Initialization.md), and [migration](./Migration.md)- support a shared set of input options. +`create-typescript-app` is built on top of [`create`](https://create.bingo). +`npx create typescript-app` supports all the flags defined by the [`create` CLI](https://www.create.bingo/cli). +It provides three Presets: -> This page uses `npx create-typescript-app` in its code examples, but initialization's `pnpm run initialize` works the same. +1. **Minimal**: Just bare starter tooling: building, formatting, linting, and type checking. +2. **Common**: Bare starters plus testing and automation for all-contributors and releases. +3. **Everything**: The most comprehensive tooling imaginable: sorting, spellchecking, and more! -## Required Options +For example, to create a new repository on the _everything_ preset: -The following required options will be prompted for interactively if not provided as CLI flags. - -### Base and Mode +```shell +npx create typescript-app --preset everything +``` -These required options determine how the creation script will set up and scaffold the repository: +`create-typescript-app` itself adds in two sections of flags: -- `--base`: Whether to scaffold the repository with: - - `minimal`: Just the bare starter tooling most repositories should ideally include - - `common`: Important additions to the minimal starters such as releases and tests - - `everything`: The most thorough tooling imaginable: sorting, spellchecking, and more! - - `prompt`: Fine-grained control over which tooling pieces to use -- `--mode`: Whether to: - - `create` a new repository in a child directory - - `initialize` a newly created repository in the current directory - - `migrate` an existing repository in the current directory +- [Base Options](#base-options) +- [Block Exclusions](#block-exclusions) -For example, scaffolding a full new repository under the current directory and also linking it to a new repository on github.com: +## Base Options -```shell -npx create-typescript-app --base everything --mode create -``` +Per [`create` > CLI > Template Options](https://www.create.bingo/cli#template-options), options defined by `create-typescript-app` may be provided on the CLI. -See [Tooling.md](./Tooling.md) for details on the tooling pieces and which bases they're included in. +### Required Base Options -### Core Options +🛑 TODO: Mention `--repository` and `--owner` in `create`, and add _"you can't override these"_ issue -These required options determine the options that will be substituted into the template's files: +These options can only be inferred when running on an existing repository. +Each will be prompted for when creating a new repository if not explicitly provided: -- `--description` _(`string`)_: Sentence case description of the repository (e.g. `Quickstart-friendly TypeScript package with lots of great repository tooling. ✨`) -- `--owner` _(`string`)_: GitHub organization or user the repository is underneath (e.g. `JoshuaKGoldberg`) -- `--repository` _(`string`)_: The kebab-case name of the repository (e.g. `create-typescript-app`) -- `--title` _(`string`)_: Title Case title for the repository (e.g. `Create TypeScript App`) +- `--description` _(`string`)_: Sentence case description of the repository +- `--title` _(`string`)_: Title Case title for the repository -For example, pre-populating all core required options and also creating a new repository: +For example, pre-populating both required base options: ```shell -npx create-typescript-app --base everything --mode create --repository testing-repository --title "Testing Title" --owner TestingOwner --description "Test Description" +npx create typescript-app --description "My awesome TypeScript app! 💖" --title "My TypeScript App" ``` That script will run completely autonomously, no prompted inputs required. ✨ -## Optional Options +### Optional Base Options -The setup scripts also allow for optional overrides of the following inputs whose defaults are based on other options: +These optional options do not need to be provided explicitly. +They will be inferred from the running user, and if migrating an existing repository, its files on disk. - `--access` _(`"public" | "restricted"`)_: Which [`npm publish --access`](https://docs.npmjs.com/cli/commands/npm-publish#access) to release npm packages with (by default, `"public"`) - `--author` _(`string`)_: Username on npm to publish packages under (by default, an existing npm author, or the currently logged in npm user, or `owner.toLowerCase()`) @@ -68,109 +63,19 @@ The setup scripts also allow for optional overrides of the following inputs whos - `--logo-width` _(`number`)_: If `--logo` is provided or detected from an existing README.md, an explicit width style (by default, read from the image, capped to `128`) - `--preserve-generated-from` _(`boolean`)_: Whether to keep the GitHub repository _generated from_ notice (by default, `false`) -For example, customizing the ownership and users associated with a new repository: - -```shell -npx create-typescript-app --author my-npm-username --email example@joshuakgoldberg.com --funding MyGitHubOrganization -``` - -> 💡 You can always manually edit files such as `package.json` after running a setup script. - -## Opt-Outs - -The setup scripts can be directed with CLI flags to opt out tooling portions and/or using API calls. - -### Excluding Tooling Portions - -The setup scripts normally will prompt you to select how much of the tooling you'd like to enable in a new repository. -Alternately, you can bypass that prompt by providing any number of the following CLI flags: - -- `--exclude-all-contributors`: Don't add all-contributors to track contributions and display them in a README.md table. -- `--exclude-build`: Don't add a build task to generate built `.js`, `.d.ts`, and related output. -- `--exclude-compliance`: Don't add a GitHub Actions workflow to verify that PRs match an expected format. -- `--exclude-lint-json`: Don't apply linting and sorting to `*.json` and `*.jsonc` files. -- `--exclude-lint-knip`: Don't add Knip to detect unused files, dependencies, and code exports. -- `--exclude-lint-md`: Don't apply linting to `*.md` files. -- `--exclude-lint-package-json`: Don't add eslint-plugin-package-json to lint for package.json correctness. -- `--exclude-lint-eslint`: Don't use eslint-plugin-eslint-comment to enforce good practices around ESLint comment directives. -- `--exclude-lint-jsdoc`: Don't use eslint-plugin-jsdoc to enforce good practices around JSDoc comments. -- `--exclude-lint-packages`: Don't add a pnpm dedupe workflow to ensure packages aren't duplicated unnecessarily. -- `--exclude-lint-perfectionist`: Don't apply eslint-plugin-perfectionist to ensure imports, keys, and so on are in sorted order. -- `--exclude-lint-regexp`: Don't add eslint-plugin-regexp to enforce good practices around regular expressions. -- `--exclude-lint-strict`: Don't augment the recommended logical lint rules with typescript-eslint's strict config. -- `--exclude-lint-stylistic`: Don't add stylistic rules such as typescript-eslint's stylistic config. -- `--exclude-lint-spelling`: Don't add cspell to spell check against dictionaries of known words. -- `--exclude-lint-yml`: Don't apply linting and sorting to `*.yaml` and `*.yml` files. -- `--exclude-releases`: Don't add release-it to generate changelogs, package bumps, and publishes based on conventional commits. -- `--exclude-renovate`: Don't add a Renovate config to dependencies up-to-date with PRs. -- `--exclude-templated-by`: Don't add a _"This package was templated with create-typescript-app"_ notice at the end of the README.md. -- `--exclude-tests`: Don't add Vitest tooling for fast unit tests, configured with coverage tracking. - -For example, initializing with all tooling except for `package.json` checks and Renovate: +For example, customizing the npm author and funding source: ```shell -npx create-typescript-app --exclude-lint-package-json --exclude-lint-packages --exclude-renovate +npx create typescript-app --author my-npm-username --funding MyGitHubOrganization ``` -> **Warning** -> Specifying any `--exclude-*` flag on the command-line will cause the setup script to skip prompting for more excludes. - -See [Tooling.md](./Tooling.md) for details on the tooling pieces and which bases they're included in. - -### Skipping API Calls - -> Alternately, see [Offline Mode](#offline-mode) to skip API calls without disabling features - -You can prevent the migration script from making some network-based changes using any or all of the following CLI flags: - -- `--skip-all-contributors-api` _(`boolean`)_: Skips network calls that fetch all-contributors data from GitHub - - This flag does nothing if `--exclude-all-contributors` was specified. -- `--skip-github-api` _(`boolean`)_: Skips calling to GitHub APIs. -- `--skip-install` _(`boolean`)_: Skips installing all the new template packages with `pnpm`. +## Block Exclusions -For example, providing all three flags will completely skip all network requests: +Per [`create` > CLI > Template Options > Block Exclusions](https://www.create.bingo/cli#block-exclusions), individual Blocks may be excluded from running. +For example, initializing with all tooling except for Renovate: ```shell -npx create-typescript-app --skip-all-contributors-api --skip-github-api --skip-install +npx create typescript-app --exclude-renovate ``` -> 💡 Tip: To temporarily preview what the script would apply without making changes on GitHub, you can run with all `--skip-*-api` flags, then `git add -A; git reset --hard HEAD` to completely reset all changes. - -### Skipping Local Changes - -You can prevent the migration script from making some changes on disk using any or all of the following CLI flags: - -- `--skip-removal` _(`boolean`)_: Skips removing setup docs and scripts, including this `docs/` directory -- `--skip-restore` _(`boolean`)_: Skips the prompt offering to restore the repository if an error occurs during setup -- `--skip-uninstall` _(`boolean`)_: Skips uninstalling packages only used for setup scripts - -For example, providing all local change skip flags: - -```shell -npx create-typescript-app --skip-removal --skip-restore --skip-uninstall -``` - -## Automatic Mode - -You can run `create-typescript-app` in an "automatic" manner with `--auto` and `--mode migrate`. -Doing so will: - -- Use the default inference for all options -- Bail out if any required [core options](#core-options) are missing - -```shell -npx create-typescript-app --auto --mode migrate -``` - -## Offline Mode - -You can run `create-typescript-app` in an "offline" manner with `--offline`. -Doing so will: - -- Enable `--exclude-all-contributors-api` and `--skip-github-api` -- Skip network calls when setting up contributors -- Run pnpm commands with pnpm's `--offline` mode - -```shell -npx create-typescript-app --offline -``` +See [Blocks.md](./Blocks.md) for the list of blocks and their corresponding presets. diff --git a/package.json b/package.json index 25174f5f4..caaa9fd58 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,6 @@ "scripts": { "build": "tsup", "format": "prettier .", - "initialize": "pnpm build --no-dts && tsx ./bin/index.js --mode initialize", "lint": "eslint . --max-warnings 0", "lint:knip": "knip", "lint:md": "markdownlint \"**/*.md\" \".github/**/*.md\" --rules sentences-per-line", @@ -32,17 +31,12 @@ "lint:spelling": "cspell \"**\" \".github/**/*\"", "prepare": "husky", "test": "vitest", - "test:create": "npx tsx script/create-test-e2e.ts", - "test:initialize": "npx tsx script/initialize-test-e2e.ts", - "test:migrate": "vitest run -r script/", "tsc": "tsc" }, "lint-staged": { "*": "prettier --ignore-unknown --write" }, "dependencies": { - "@clack/prompts": "^0.9.0", - "@prettier/sync": "^0.5.2", "chalk": "^5.4.1", "create": "0.1.0-alpha.11", "cspell-populate-words": "^0.3.0", @@ -58,25 +52,18 @@ "lazy-value": "^3.0.0", "npm-user": "^6.1.1", "object-strings-deep": "^0.1.1", - "octokit": "^4.0.2", - "octokit-from-auth": "^0.3.0", "parse-author": "^2.0.0", "parse-package-name": "^1.0.0", - "populate-all-contributors-for-repository": "^0.1.2", "prettier": "^3.4.2", "remove-undefined-objects": "^5.0.0", - "replace-in-file": "^8.3.0", - "rimraf": "^6.0.1", "set-github-repository-labels": "^0.1.0", "sort-package-json": "^2.12.0", "title-case": "^4.3.2", - "zod": "^3.24.1", - "zod-validation-error": "^3.4.0" + "zod": "^3.24.1" }, "devDependencies": { "@eslint-community/eslint-plugin-eslint-comments": "4.4.1", "@eslint/js": "9.17.0", - "@octokit/request-error": "6.1.5", "@release-it/conventional-changelog": "9.0.3", "@types/eslint-plugin-markdown": "2.0.2", "@types/git-url-parse": "9.0.3", @@ -87,7 +74,6 @@ "@vitest/coverage-v8": "2.1.8", "@vitest/eslint-plugin": "1.1.20", "all-contributors-cli": "6.26.1", - "c8": "10.1.3", "console-fail-test": "0.5.0", "create-testers": "0.1.0-alpha.11", "cspell": "8.17.1", @@ -100,7 +86,6 @@ "eslint-plugin-perfectionist": "4.4.0", "eslint-plugin-regexp": "2.7.0", "eslint-plugin-yml": "1.16.0", - "globby": "14.0.2", "husky": "9.1.7", "knip": "5.41.1", "lint-staged": "15.3.0", @@ -112,7 +97,6 @@ "release-it": "17.10.0", "sentences-per-line": "0.3.0", "tsup": "8.3.5", - "tsx": "4.19.2", "typescript": "5.7.2", "typescript-eslint": "8.19.0", "vitest": "2.1.8" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 668e7cc19..325da0b11 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,12 +8,6 @@ importers: .: dependencies: - '@clack/prompts': - specifier: ^0.9.0 - version: 0.9.0 - '@prettier/sync': - specifier: ^0.5.2 - version: 0.5.2(prettier@3.4.2) chalk: specifier: ^5.4.1 version: 5.4.1 @@ -59,33 +53,18 @@ importers: object-strings-deep: specifier: ^0.1.1 version: 0.1.1 - octokit: - specifier: ^4.0.2 - version: 4.0.2 - octokit-from-auth: - specifier: ^0.3.0 - version: 0.3.0 parse-author: specifier: ^2.0.0 version: 2.0.0 parse-package-name: specifier: ^1.0.0 version: 1.0.0 - populate-all-contributors-for-repository: - specifier: ^0.1.2 - version: 0.1.2 prettier: specifier: ^3.4.2 version: 3.4.2 remove-undefined-objects: specifier: ^5.0.0 version: 5.0.0 - replace-in-file: - specifier: ^8.3.0 - version: 8.3.0 - rimraf: - specifier: ^6.0.1 - version: 6.0.1 set-github-repository-labels: specifier: ^0.1.0 version: 0.1.0 @@ -98,9 +77,6 @@ importers: zod: specifier: ^3.24.1 version: 3.24.1 - zod-validation-error: - specifier: ^3.4.0 - version: 3.4.0(zod@3.24.1) devDependencies: '@eslint-community/eslint-plugin-eslint-comments': specifier: 4.4.1 @@ -108,9 +84,6 @@ importers: '@eslint/js': specifier: 9.17.0 version: 9.17.0 - '@octokit/request-error': - specifier: 6.1.5 - version: 6.1.5 '@release-it/conventional-changelog': specifier: 9.0.3 version: 9.0.3(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.0.0)(release-it@17.10.0(typescript@5.7.2)) @@ -141,9 +114,6 @@ importers: all-contributors-cli: specifier: 6.26.1 version: 6.26.1 - c8: - specifier: 10.1.3 - version: 10.1.3 console-fail-test: specifier: 0.5.0 version: 0.5.0 @@ -180,9 +150,6 @@ importers: eslint-plugin-yml: specifier: 1.16.0 version: 1.16.0(eslint@9.17.0(jiti@2.4.0)) - globby: - specifier: 14.0.2 - version: 14.0.2 husky: specifier: 9.1.7 version: 9.1.7 @@ -216,9 +183,6 @@ importers: tsup: specifier: 8.3.5 version: 8.3.5(jiti@2.4.0)(postcss@8.4.36)(tsx@4.19.2)(typescript@5.7.2)(yaml@2.6.1) - tsx: - specifier: 4.19.2 - version: 4.19.2 typescript: specifier: 5.7.2 version: 5.7.2 @@ -286,10 +250,6 @@ packages: '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - '@bcoe/v8-coverage@1.0.1': - resolution: {integrity: sha512-W+a0/JpU28AqH4IKtwUPcEUnUyXMDLALcn5/JLczGGT9fHE2sIby/xP/oQnx3nxkForzgzPy201RAKcB4xPAFQ==} - engines: {node: '>=18'} - '@clack/core@0.4.0': resolution: {integrity: sha512-YJCYBsyJfNDaTbvDUVSJ3SgSuPrcujarRgkJ5NLjexDZKvaOiVVJvAQYx8lIgG0qRT8ff0fPgqyBCVivanIZ+A==} @@ -1221,11 +1181,6 @@ packages: resolution: {integrity: sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==} engines: {node: '>=12'} - '@prettier/sync@0.5.2': - resolution: {integrity: sha512-Yb569su456XNx5BsH/Vyem7xD6g/y9iLmLUzRKM1a/dhU/D7HqqvkAG72znulXlMXztbV0iiu9O5AL8K98TzZQ==} - peerDependencies: - prettier: '*' - '@release-it/conventional-changelog@9.0.3': resolution: {integrity: sha512-+3TL+B89Kc+VTbfGxpTvJkbegWt5XIzkovsYVJyoZpOZDG07v25FU8c5R5Q8yNUs76Ikfq0sp+ZTTxmefG4Hiw==} engines: {node: ^18.18.0 || ^20.9.0 || ^22.0.0} @@ -1365,9 +1320,6 @@ packages: '@types/html-to-text@9.0.4': resolution: {integrity: sha512-pUY3cKH/Nm2yYrEmDlPR1mR7yszjGx4DrwPjQ702C4/D5CwHuZTgZdIdwPkRbcuhs7BAh2L5rg3CL5cbRiGTCQ==} - '@types/istanbul-lib-coverage@2.0.4': - resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} - '@types/js-yaml@4.0.9': resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} @@ -1532,10 +1484,6 @@ packages: engines: {node: '>=4'} hasBin: true - all-contributors-for-repository@0.4.0: - resolution: {integrity: sha512-qGJHdVaRnoKMEZ6IcDvrFQJIDA9KPSsCVAMk+mjqVkHMYrgvp05TU94I8x4SjwWIfYBLp9heC9Onh55BIrOA4A==} - engines: {node: '>=18'} - ansi-align@3.0.1: resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} @@ -1662,24 +1610,10 @@ packages: peerDependencies: esbuild: '>=0.18' - c8@10.1.3: - resolution: {integrity: sha512-LvcyrOAaOnrrlMpW22n690PUvxiq4Uf9WMhQwNJ9vgagkL/ph1+D4uvjvDA5XCbykrc0sx+ay6pVi9YZ1GnhyA==} - engines: {node: '>=18'} - hasBin: true - peerDependencies: - monocart-coverage-reports: ^2 - peerDependenciesMeta: - monocart-coverage-reports: - optional: true - cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} - cached-factory@0.0.2: - resolution: {integrity: sha512-4mhebGQ8YyvlDAX+zOHso5MezPi2pP11ZFE7vYhDIOJifsxEi7R5geB/nrLBYzQH5nwZ9kNsLAjM+WBj6osLDg==} - engines: {node: '>=18'} - cached-factory@0.1.0: resolution: {integrity: sha512-IGOSWu+NuED5UzCRmBeqQPZ8z7SkgrD/nN67W2iY1Qv83CVhevyMexkGclJ86saXisIqxoOnbeiTWvsCHRqJBw==} engines: {node: '>=18'} @@ -1811,10 +1745,6 @@ packages: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} - co-author-to-username@0.1.1: - resolution: {integrity: sha512-KtQvKq2m52MncBa9hmYSiUae/hRRjLIOpXY29Ll43mSK/KreIRcoNy+K5/U5UHyVMNNjux1Xi4Ya744BO4G+RA==} - engines: {node: '>=18'} - color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} @@ -1943,9 +1873,6 @@ packages: engines: {node: '>=18'} hasBin: true - convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} @@ -2089,10 +2016,6 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} - description-to-co-authors@0.3.0: - resolution: {integrity: sha512-GaMDuvOCmtkQ3tjjawAEl3YhxNEIsR9tmlXV4xeypGyiO75YLXRm3iHKltqrmxNgtNlppVzT2pdsPM2Yzm67CA==} - engines: {node: '>=18.3.0'} - detect-indent@6.1.0: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} @@ -3106,9 +3029,6 @@ packages: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} - make-synchronized@0.2.9: - resolution: {integrity: sha512-4wczOs8SLuEdpEvp3vGo83wh8rjJ78UsIk7DIX5fxdfmfMJGog4bQzxfvOwq7Q3yCHLC4jp1urPHIxRS/A93gA==} - markdown-it@14.1.0: resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} hasBin: true @@ -3364,10 +3284,6 @@ packages: resolution: {integrity: sha512-UMo+WQkWi1PS/HZ15SMM1sgPhhXykqPJKCCAi6R0+JAhTbLBcfeKCJ3AwUVdJBJQKSQ37BUqPpME4iGAjZQtkw==} engines: {node: '>=18.3.0'} - octokit-from-auth@0.3.0: - resolution: {integrity: sha512-HeS+YVPExV5nkdRZyQQRepZVV+pL4ahVmh9KfKXLAbNc3D82G2xeuSWfKzHSIlqB1taQnZYPJWdpfcIe45d1lw==} - engines: {node: '>=18.3.0'} - octokit@4.0.2: resolution: {integrity: sha512-wbqF4uc1YbcldtiBFfkSnquHtECEIpYD78YUXI6ri1Im5OO2NLo6ZVpRdbJpdnpZ05zMrVPssNiEo6JQtea+Qg==} engines: {node: '>= 18'} @@ -3580,11 +3496,6 @@ packages: resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} engines: {node: '>= 6'} - populate-all-contributors-for-repository@0.1.2: - resolution: {integrity: sha512-PQ93fI7OpetHtW0c+iu6Dx2aaiRurj07LTu51pAw/qkky0eb1CdcZ+4Y+DYbzZXId4UhHGH/ednm46Pwf1P8FA==} - engines: {node: '>=18.3.0'} - hasBin: true - postcss-load-config@6.0.1: resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} engines: {node: '>= 18'} @@ -3732,11 +3643,6 @@ packages: resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} engines: {node: '>=0.10'} - replace-in-file@8.3.0: - resolution: {integrity: sha512-4VhddQiMCPIuypiwHDTM+XHjZoVu9h7ngBbSCnwGRcwdHwxltjt/m//Ep3GDwqaOx1fDSrKFQ+n7uo4uVcEz9Q==} - engines: {node: '>=18'} - hasBin: true - require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -3778,11 +3684,6 @@ packages: rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rimraf@6.0.1: - resolution: {integrity: sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==} - engines: {node: 20 || >=22} - hasBin: true - rollup@4.28.0: resolution: {integrity: sha512-G9GOrmgWHBma4YfCcX8PjH0qhXSdH8B4HDE2o4/jaxj93S4DPCIDoLcXz99eWMji4hB29UFCEd7B2gwGJDR9cQ==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -4225,10 +4126,6 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - v8-to-istanbul@9.2.0: - resolution: {integrity: sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==} - engines: {node: '>=10.12.0'} - validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} @@ -4497,8 +4394,6 @@ snapshots: '@bcoe/v8-coverage@0.2.3': {} - '@bcoe/v8-coverage@1.0.1': {} - '@clack/core@0.4.0': dependencies: picocolors: 1.1.1 @@ -5264,11 +5159,6 @@ snapshots: '@pnpm/network.ca-file': 1.0.2 config-chain: 1.1.13 - '@prettier/sync@0.5.2(prettier@3.4.2)': - dependencies: - make-synchronized: 0.2.9 - prettier: 3.4.2 - '@release-it/conventional-changelog@9.0.3(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.0.0)(release-it@17.10.0(typescript@5.7.2))': dependencies: concat-stream: 2.0.0 @@ -5376,8 +5266,6 @@ snapshots: '@types/html-to-text@9.0.4': {} - '@types/istanbul-lib-coverage@2.0.4': {} - '@types/js-yaml@4.0.9': {} '@types/json-schema@7.0.15': {} @@ -5595,14 +5483,6 @@ snapshots: transitivePeerDependencies: - encoding - all-contributors-for-repository@0.4.0: - dependencies: - co-author-to-username: 0.1.1 - conventional-commits-parser: 6.0.0 - description-to-co-authors: 0.3.0 - octokit: 4.0.2 - octokit-from-auth: 0.3.0 - ansi-align@3.0.1: dependencies: string-width: 4.2.3 @@ -5722,24 +5602,8 @@ snapshots: esbuild: 0.24.0 load-tsconfig: 0.2.5 - c8@10.1.3: - dependencies: - '@bcoe/v8-coverage': 1.0.1 - '@istanbuljs/schema': 0.1.3 - find-up: 5.0.0 - foreground-child: 3.3.0 - istanbul-lib-coverage: 3.2.2 - istanbul-lib-report: 3.0.1 - istanbul-reports: 3.1.7 - test-exclude: 7.0.1 - v8-to-istanbul: 9.2.0 - yargs: 17.7.2 - yargs-parser: 21.1.1 - cac@6.7.14: {} - cached-factory@0.0.2: {} - cached-factory@0.1.0: {} callsites@3.1.0: {} @@ -5862,11 +5726,6 @@ snapshots: clone@1.0.4: {} - co-author-to-username@0.1.1: - dependencies: - cached-factory: 0.0.2 - octokit: 4.0.2 - color-convert@1.9.3: dependencies: color-name: 1.1.3 @@ -6006,8 +5865,6 @@ snapshots: conventional-commits-parser: 6.0.0 meow: 13.2.0 - convert-source-map@2.0.0: {} - core-util-is@1.0.3: {} cosmiconfig@9.0.0(typescript@5.7.2): @@ -6199,8 +6056,6 @@ snapshots: dequal@2.0.3: {} - description-to-co-authors@0.3.0: {} - detect-indent@6.1.0: {} detect-indent@7.0.1: {} @@ -6326,6 +6181,7 @@ snapshots: '@esbuild/win32-arm64': 0.23.0 '@esbuild/win32-ia32': 0.23.0 '@esbuild/win32-x64': 0.23.0 + optional: true esbuild@0.24.0: optionalDependencies: @@ -7371,8 +7227,6 @@ snapshots: dependencies: semver: 7.6.3 - make-synchronized@0.2.9: {} - markdown-it@14.1.0: dependencies: argparse: 2.0.1 @@ -7731,11 +7585,6 @@ snapshots: get-github-auth-token: 0.1.0 octokit: 4.0.2 - octokit-from-auth@0.3.0: - dependencies: - get-github-auth-token: 0.1.0 - octokit: 4.0.2 - octokit@4.0.2: dependencies: '@octokit/app': 15.0.1 @@ -7987,14 +7836,6 @@ snapshots: pirates@4.0.6: {} - populate-all-contributors-for-repository@0.1.2: - dependencies: - all-contributors-cli: 6.26.1 - all-contributors-for-repository: 0.4.0 - execa: 9.5.2 - transitivePeerDependencies: - - encoding - postcss-load-config@6.0.1(jiti@2.4.0)(postcss@8.4.36)(tsx@4.19.2)(yaml@2.6.1): dependencies: lilconfig: 3.1.3 @@ -8162,12 +8003,6 @@ snapshots: repeat-string@1.6.1: {} - replace-in-file@8.3.0: - dependencies: - chalk: 5.4.1 - glob: 10.4.5 - yargs: 17.7.2 - require-directory@2.1.1: {} require-main-filename@2.0.0: {} @@ -8200,11 +8035,6 @@ snapshots: rfdc@1.4.1: {} - rimraf@6.0.1: - dependencies: - glob: 11.0.0 - package-json-from-dist: 1.0.1 - rollup@4.28.0: dependencies: '@types/estree': 1.0.6 @@ -8564,6 +8394,7 @@ snapshots: get-tsconfig: 4.8.1 optionalDependencies: fsevents: 2.3.3 + optional: true type-check@0.4.0: dependencies: @@ -8633,12 +8464,6 @@ snapshots: util-deprecate@1.0.2: {} - v8-to-istanbul@9.2.0: - dependencies: - '@jridgewell/trace-mapping': 0.3.25 - '@types/istanbul-lib-coverage': 2.0.4 - convert-source-map: 2.0.0 - validate-npm-package-license@3.0.4: dependencies: spdx-correct: 3.1.1 diff --git a/script/__snapshots__/migrate-test-e2e.ts.snap b/script/__snapshots__/migrate-test-e2e.ts.snap deleted file mode 100644 index 9806ef36d..000000000 --- a/script/__snapshots__/migrate-test-e2e.ts.snap +++ /dev/null @@ -1,273 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`expected file changes > .github/renovate.json 1`] = ` -"--- a/.github/renovate.json -+++ b/.github/renovate.json -@@ ... @@ - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "automerge": true, - "extends": ["config:best-practices", "replacements:all"], -- "github-actions": { "enabled": false }, - "ignoreDeps": ["codecov/codecov-action"], - "labels": ["dependencies"], - "minimumReleaseAge": "7 days"," -`; - -exports[`expected file changes > .github/workflows/ci.yml 1`] = ` -"--- a/.github/workflows/ci.yml -+++ b/.github/workflows/ci.yml -@@ ... @@ - jobs: -- are_the_types_wrong: -- name: Are The Types Wrong? -- runs-on: ubuntu-latest -- steps: -- - uses: actions/checkout@v4 -- - uses: ./.github/actions/prepare -- - run: pnpm build -- - run: npx --yes @arethetypeswrong/cli --pack . --ignore-rules cjs-resolves-to-esm - build: - name: Build - runs-on: ubuntu-latest -@@ ... @@ jobs: - - uses: actions/checkout@v4 - - uses: ./.github/actions/prepare - - run: pnpm build -- - run: node ./lib/index.js --version -+ - run: node ./lib/index.js - lint: - name: Lint - runs-on: ubuntu-latest -@@ ... @@ jobs: - - uses: actions/checkout@v4 - - uses: ./.github/actions/prepare - - run: pnpm run test --coverage -- - env: -- CODECOV_TOKEN: \${{ secrets.CODECOV_TOKEN }} -- if: always() -- uses: codecov/codecov-action@v3 -- with: -- flags: unit -- test_creation_script: -- name: Test Creation Script -- runs-on: ubuntu-latest -- steps: -- - uses: actions/checkout@v4 -- - uses: ./.github/actions/prepare -- - run: pnpm run build -- - run: pnpm run test:create -- - env: -- CODECOV_TOKEN: \${{ secrets.CODECOV_TOKEN }} -- if: always() -- uses: codecov/codecov-action@v3 -- with: -- files: coverage-create/lcov.info -- flags: create -- test_initialization_script: -- name: Test Initialization Script -- runs-on: ubuntu-latest -- steps: -- - uses: actions/checkout@v4 -- - uses: ./.github/actions/prepare -- - run: pnpm run build -- - run: pnpm run test:initialize -- - env: -- CODECOV_TOKEN: \${{ secrets.CODECOV_TOKEN }} -- if: always() -- uses: codecov/codecov-action@v3 -- with: -- files: coverage-initialize/lcov.info -- flags: initialize -- test_migration_script: -- name: Test Migration Script -- runs-on: ubuntu-latest -- steps: -- - uses: actions/checkout@v4 -- - uses: ./.github/actions/prepare -- - run: pnpm run build -- - run: pnpm run test:migrate -- - env: -- CODECOV_TOKEN: \${{ secrets.CODECOV_TOKEN }} -- if: always() -+ - if: always() - uses: codecov/codecov-action@v3 -- with: -- files: coverage-migrate/lcov.info -- flags: migrate - type_check: - name: Type Check - runs-on: ubuntu-latest" -`; - -exports[`expected file changes > .gitignore 1`] = ` -"--- a/.gitignore -+++ b/.gitignore -@@ ... @@ --/coverage* -+/coverage - /lib - /node_modules" -`; - -exports[`expected file changes > .prettierignore 1`] = ` -"--- a/.prettierignore -+++ b/.prettierignore -@@ ... @@ - /.husky --/coverage* -+/coverage - /lib - /pnpm-lock.yaml" -`; - -exports[`expected file changes > README.md 1`] = ` -"--- a/README.md -+++ b/README.md -@@ ... @@ -

Create TypeScript App

- --

Quickstart-friendly TypeScript template with comprehensive, configurable, opinionated tooling. 🎁

-+

A very lovely package. Hooray!

- -

- -@@ ... @@ Thanks! 💖 - - - -+ -+ -+ -+> 💙 This package was templated with [\`create-typescript-app\`](https://github.com/JoshuaKGoldberg/create-typescript-app)." -`; - -exports[`expected file changes > eslint.config.js 1`] = ` -"--- a/eslint.config.js -+++ b/eslint.config.js -@@ ... @@ --/* --👋 Hi! This ESLint configuration contains a lot more stuff than many repos'! --You can read from it to see all sorts of linting goodness, but don't worry - --it's not something you need to exhaustively understand immediately. 💙 -- --If you're interested in learning more, see the 'getting started' docs on: --- ESLint: https://eslint.org --- typescript-eslint: https://typescript-eslint.io --*/ -- - import comments from "@eslint-community/eslint-plugin-eslint-comments/configs"; - import eslint from "@eslint/js"; - import vitest from "@vitest/eslint-plugin"; -@@ ... @@ import tseslint from "typescript-eslint"; - - export default tseslint.config( - { -- ignores: [ -- "**/*.snap", -- "coverage*", -- "lib", -- "node_modules", -- "pnpm-lock.yaml", -- ], -+ ignores: ["**/*.snap", "coverage", "lib", "node_modules", "pnpm-lock.yaml"], - }, - { linterOptions: { reportUnusedDisableDirectives: "error" } }, - eslint.configs.recommended, -@@ ... @@ export default tseslint.config( - files: ["**/*.js", "**/*.ts"], - languageOptions: { - parserOptions: { -- projectService: { -- allowDefaultProject: ["*.config.*s", "bin/*.js"], -- }, -+ projectService: { allowDefaultProject: ["*.config.*s"] }, - tsconfigRootDir: import.meta.dirname, - }, - }, - rules: { -- // These on-by-default rules work well for this repo if configured -- "@typescript-eslint/no-unnecessary-condition": [ -- "error", -- { -- allowConstantLoopConditions: true, -- }, -- ], -- "@typescript-eslint/prefer-nullish-coalescing": [ -- "error", -- { ignorePrimitives: true }, -- ], -- "@typescript-eslint/restrict-template-expressions": [ -- "error", -- { allowBoolean: true, allowNullish: true, allowNumber: true }, -- ], -- "n/no-unsupported-features/node-builtins": [ -- "error", -- { allowExperimental: true }, -- ], -- - // Stylistic concerns that don't interfere with Prettier - "logical-assignment-operators": [ - "error", -@@ ... @@ export default tseslint.config( - { - extends: [tseslint.configs.disableTypeChecked], - files: ["**/*.md/*.ts"], -+ rules: { -+ "n/no-missing-import": [ -+ "error", -+ { allowModules: ["create-typescript-app"] }, -+ ], -+ }, - }, - { - extends: [vitest.configs.recommended]," -`; - -exports[`expected file changes > knip.json 1`] = ` -"--- a/knip.json -+++ b/knip.json -@@ ... @@ - { - "$schema": "https://unpkg.com/knip@5.41.1/schema.json", -- "entry": ["script/*e2e.js", "src/index.ts!", "src/**/*.test.*"], -- "ignoreDependencies": ["all-contributors-cli", "cspell-populate-words"], -+ "entry": ["src/index.ts!"], - "ignoreExportsUsedInFile": { "interface": true, "type": true }, -- "project": ["src/**/*.ts!", "script/**/*.js"] -+ "project": ["src/**/*.ts!"] - }" -`; - -exports[`expected file changes > package.json 1`] = ` -"--- a/package.json -+++ b/package.json -@@ ... @@ - { - "name": "create-typescript-app", - "version": "1.79.0", -- "description": "Quickstart-friendly TypeScript template with comprehensive, configurable, opinionated tooling. 🎁", -+ "description": "A very lovely package. Hooray! 💖", - "repository": { - "type": "git", - "url": "https://github.com/JoshuaKGoldberg/create-typescript-app" -@@ ... @@ - "lint-staged": "15.2.11", - "markdownlint": "0.37.2", - "markdownlint-cli": "0.43.0", -+ "prettier": "^3.4.2", - "prettier-plugin-curly": "0.3.1", - "prettier-plugin-packagejson": "2.5.6", - "prettier-plugin-sh": "0.14.0"," -`; - -exports[`expected file changes > tsconfig.json 1`] = ` -"--- a/tsconfig.json -+++ b/tsconfig.json -@@ ... @@ - "strict": true, - "target": "ES2022" - }, -- "include": ["src", "script"] -+ "include": ["src"] - }" -`; diff --git a/script/create-test-e2e.ts b/script/create-test-e2e.ts deleted file mode 100644 index 2419e8b9a..000000000 --- a/script/create-test-e2e.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { $, execaCommand } from "execa"; -import { strict as assert } from "node:assert"; -import { rimraf } from "rimraf"; - -const author = "Test Author"; -const description = "Test description."; -const email = "test@email.com"; -const repository = "testing-repository"; -const owner = "TestOwner"; -const title = "Test Title"; - -await rimraf(["coverage*", repository]); - -// Fist we run with --mode create to create a new new local repository, -// asserting that pnpm i passes in that repository's directory. -await $({ - stdio: "inherit", -})`c8 -o ./coverage-create -r html -r lcov --src src node ./bin/index.js --base everything --mode create --author ${author} --email ${email} --description ${description} --owner ${owner} --title ${title} --repository ${repository} --skip-all-contributors-api --skip-github-api`; - -process.chdir(repository); - -const failures = []; - -// Then we run each of the CI commands to assert that they pass too. -for (const command of [ - `pnpm i`, - `pnpm run build`, - `pnpm run format --list-different`, - `pnpm run lint`, - `pnpm run lint:md`, - `pnpm run lint:packages`, - `pnpm run lint:spelling`, - `pnpm run lint:knip`, - `pnpm run test run`, - `pnpm run tsc`, -]) { - const result = await execaCommand(command, { stdio: "inherit" }); - - if (result.exitCode) { - failures.push({ command, result }); - } -} - -assert.deepEqual(failures, []); diff --git a/script/initialize-test-e2e.ts b/script/initialize-test-e2e.ts deleted file mode 100644 index a64be02ec..000000000 --- a/script/initialize-test-e2e.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { $ } from "execa"; -import { globby } from "globby"; -import { strict as assert } from "node:assert"; -import * as fs from "node:fs/promises"; -import { rimraf } from "rimraf"; - -const description = "New Description Test"; -const owner = "RNR1"; -const title = "New Title Test"; -const repository = "new-repository-test"; - -await rimraf("coverage*"); - -// Fist we run with --mode initialize to modify the local repository files, -// asserting that the created package.json keeps the general description. -await $({ - stdio: "inherit", -})`pnpm run initialize --base everything --mode initialize --description ${description} --owner ${owner} --title ${title} --repository ${repository} --skip-all-contributors-api --skip-github-api --skip-restore`; - -const newPackageJson = JSON.parse( - (await fs.readFile("./package.json")).toString(), -) as Record; -console.log("New package JSON:", newPackageJson); - -assert.equal(newPackageJson.description, description); -assert.equal(newPackageJson.name, repository); - -// Assert that the initialize script used the provided values in files, -// except for the 'This package was templated with ...' attribution notice. -const files = await globby(["*.*", "**/*.*"], { - gitignore: true, - ignoreFiles: ["script/initialize-test-e2e.js"], -}); - -for (const search of [`/JoshuaKGoldberg/`, "create-typescript-app"]) { - const { stdout } = await $`grep -i ${search} ${files}`; - assert.equal( - stdout, - `README.md:> 💙 This package was templated with [\`create-typescript-app\`](https://github.com/JoshuaKGoldberg/create-typescript-app).`, - ); -} - -// Use Knip to assert that none of the template-only dependencies remain. -// They should have been removed as part of initialization. -try { - await $`pnpm run lint:knip`; -} catch (error) { - throw new Error( - `Error running lint:knip: ${(error as Error).stack ?? (error as string)}`, - ); -} - -// Now that initialize has passed normal steps, we reset everything, -// then run again without removing files - so we can capture test coverage -await $`git add -A`; -await $`git reset --hard HEAD`; -await $`pnpm i`; -await $`pnpm run build`; -await $({ - stdio: "inherit", -})`c8 -o ./coverage -r html -r lcov --src src node ./bin/index.js --base everything --mode initialize --description ${description} --owner ${owner} --title ${title} --repository ${repository} --skip-all-contributors-api --skip-github-api --skip-removal --skip-restore`; diff --git a/script/migrate-test-e2e.ts b/script/migrate-test-e2e.ts deleted file mode 100644 index e713d67be..000000000 --- a/script/migrate-test-e2e.ts +++ /dev/null @@ -1,151 +0,0 @@ -import chalk from "chalk"; -import { $, execaCommand } from "execa"; -import * as fs from "node:fs/promises"; -import { rimraf } from "rimraf"; -import { assert, describe, expect, test } from "vitest"; - -const filesExpectedToBeChanged = [ - "README.md", - "knip.json", - ".github/renovate.json", - ".github/workflows/ci.yml", - ".gitignore", - ".prettierignore", - "eslint.config.js", - "package.json", - "tsconfig.json", -]; - -const filesThatMightBeChanged = new Set([ - // For now, ignore typos cspell is picking up from migration snapshots. - "cspell.json", - - "script/__snapshots__/migrate-test-e2e.ts.snap", - ...filesExpectedToBeChanged, -]); - -await rimraf("coverage*"); - -const originalReadme = (await fs.readFile("README.md")).toString(); - -const originalSnapshots = ( - await fs.readFile("script/__snapshots__/migrate-test-e2e.ts.snap") -).toString(); - -await $({ - stdio: "inherit", -})`c8 -o ./coverage -r html -r lcov --src src node ./bin/index.js --auto --mode migrate --skip-all-contributors-api --skip-github-api --skip-install`; - -// All Contributors seems to not be using Prettier to format files... -await fs.writeFile( - ".all-contributorsrc", - JSON.stringify( - JSON.parse((await fs.readFile(".all-contributorsrc")).toString()), - null, - 2, - ) + "\n", -); - -// Ignore changes to the README.md all-contributor count and contributors table... -const updatedReadme = (await fs.readFile("README.md")).toString(); -await fs.writeFile( - "README.md", - [ - updatedReadme.slice(0, updatedReadme.indexOf("## Contributors")) + - originalReadme.slice( - originalReadme.indexOf("## Contributors"), - originalReadme.indexOf(""), - ), - updatedReadme.slice(updatedReadme.indexOf("")), - ] - .join("") - .replace( - /All Contributors: \d+/g, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - /All Contributors: \d+/.exec(originalReadme)![0], - ) - .replace( - /all_contributors-\d+/g, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - /all_contributors-\d+/.exec(originalReadme)![0], - ), -); - -// ...and even to the snapshot file, so diffs don't mind it. -await fs.writeFile( - "script/__snapshots__/migrate-test-e2e.ts.snap", - originalSnapshots - .replace( - /All Contributors: \d+/g, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - /All Contributors: \d+/.exec(originalReadme)![0], - ) - .replace( - /all_contributors-\d+/g, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - /all_contributors-\d+/.exec(originalReadme)![0], - ), -); - -describe("expected file changes", () => { - test.each(filesExpectedToBeChanged)("%s", async (file) => { - const { stdout } = await execaCommand(`git diff HEAD -- ${file}`); - const contentsAfterGitMarkers = stdout - .split("\n") - .slice(2) - .join("\n") - .replace(/@@ -\d+,\d+ \+\d+,\d+ @@/g, "@@ ... @@"); - - assert( - stdout, - `Looks like there were no changes to ${file} from migration?`, - ); - - // If this fails, see .github/DEVELOPMENT.md > Setup Scripts for context. - // Then see .github/DEVELOPMENT.md > Migration Snapshot Failures. - expect(contentsAfterGitMarkers).toMatchSnapshot(); - }); -}); - -test("unexpected file changes", async () => { - const { stdout: gitStatus } = await $`git status`; - console.log(`Stdout from running \`git status\`:\n${gitStatus}`); - - const indexOfUnstagedFilesMessage = gitStatus.indexOf( - "Changes not staged for commit:", - ); - assert( - indexOfUnstagedFilesMessage !== -1, - `Looks like migrate didn't cause any file changes? That's ...probably incorrect? 😬`, - ); - - const unstagedModifiedFiles = gitStatus - .slice(indexOfUnstagedFilesMessage) - .match(/modified: {3}\S+\n/g) - ?.map((match) => match.split(/\s+/)[1]) - .filter((filePath) => !filesThatMightBeChanged.has(filePath)); - - console.log("Unexpected modified files are:", unstagedModifiedFiles); - - if (unstagedModifiedFiles?.length) { - const gitDiffCommand = `git diff HEAD -- ${unstagedModifiedFiles.join( - " ", - )}`; - const { stdout } = await execaCommand(gitDiffCommand); - - console.log(`Stdout from running \`${gitDiffCommand}\`:\n${stdout}`); - - throw new Error( - [ - "", - "Oh no! Running the migrate script unexpectedly modified:", - ...unstagedModifiedFiles.map((filePath) => ` - ${filePath}`), - "", - "See .github/DEVELOPMENT.md > Setup Scripts for context.", - "Then see .github/DEVELOPMENT.md > Unexpected File Modifications.", - ] - .map((line) => chalk.red(line)) - .join("\n"), - ); - } -}); diff --git a/script/vitest.config.ts b/script/vitest.config.ts deleted file mode 100644 index 663590231..000000000 --- a/script/vitest.config.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { defineConfig } from "vitest/config"; - -export default defineConfig({ test: { include: ["./migrate-test-e2e.ts"] } }); diff --git a/src/base.test.ts b/src/base.test.ts new file mode 100644 index 000000000..ca22a4735 --- /dev/null +++ b/src/base.test.ts @@ -0,0 +1,53 @@ +import { produceBase } from "create"; +import { readFile } from "fs/promises"; +import { describe, expect, test } from "vitest"; + +import { base } from "./base.js"; +import { AllContributorsData } from "./types.js"; + +describe("base", () => { + test("production from create-typescript-app", async () => { + const options = await produceBase(base); + + expect(options).toEqual({ + access: "public", + author: "Josh Goldberg ✨", + bin: "./bin/index.js", + contributors: ( + JSON.parse( + (await readFile(".all-contributorsrc")).toString(), + ) as AllContributorsData + ).contributors, + description: + "Quickstart-friendly TypeScript template with comprehensive, configurable, opinionated tooling. 🎁", + documentation: "", + email: { + github: "github@joshuakgoldberg.com", + npm: "npm@joshuakgoldberg.com", + }, + funding: "JoshuaKGoldberg", + guide: { + href: "https://www.joshuakgoldberg.com/blog/contributing-to-a-create-typescript-app-repository", + title: "Contributing to a create-typescript-app Repository", + }, + login: "Josh Goldberg ✨", + logo: { + alt: "Project logo: the TypeScript blue square with rounded corners, but a plus sign instead of 'TS'", + height: 128, + src: "./docs/create-typescript-app.png", + type: "png", + width: 128, + }, + node: { + minimum: expect.any(String), + pinned: expect.any(String), + }, + owner: "JoshuaKGoldberg", + packageData: expect.any(Object), + repository: "create-typescript-app", + title: "Create TypeScript App", + usage: expect.any(String), + version: expect.any(String), + }); + }); +}); diff --git a/src/next/base.ts b/src/base.ts similarity index 88% rename from src/next/base.ts rename to src/base.ts index 80797ab13..f94d5ae43 100644 --- a/src/next/base.ts +++ b/src/base.ts @@ -9,18 +9,18 @@ import lazyValue from "lazy-value"; import npmUser from "npm-user"; import { z } from "zod"; -import { parsePackageAuthor } from "../shared/options/createOptionDefaults/parsePackageAuthor.js"; -import { readDefaultsFromReadme } from "../shared/options/createOptionDefaults/readDefaultsFromReadme.js"; -import { readEmails } from "../shared/options/createOptionDefaults/readEmails.js"; -import { readFunding } from "../shared/options/createOptionDefaults/readFunding.js"; -import { readGuide } from "../shared/options/createOptionDefaults/readGuide.js"; -import { readPackageData } from "../shared/packages.js"; -import { readFileSafe } from "../shared/readFileSafe.js"; -import { tryCatchLazyValueAsync } from "../shared/tryCatchLazyValueAsync.js"; -import { AllContributorsData } from "../shared/types.js"; -import { readDescription } from "./readDescription.js"; -import { readDocumentation } from "./readDocumentation.js"; +import { parsePackageAuthor } from "./options/parsePackageAuthor.js"; +import { readDefaultsFromReadme } from "./options/readDefaultsFromReadme.js"; +import { readDescription } from "./options/readDescription.js"; +import { readDocumentation } from "./options/readDocumentation.js"; +import { readEmails } from "./options/readEmails.js"; +import { readFileSafe } from "./options/readFileSafe.js"; +import { readFunding } from "./options/readFunding.js"; +import { readGuide } from "./options/readGuide.js"; +import { readPackageData } from "./options/readPackageData.js"; +import { AllContributorsData } from "./types.js"; import { swallowError } from "./utils/swallowError.js"; +import { tryCatchLazyValueAsync } from "./utils/tryCatchLazyValueAsync.js"; export const base = createBase({ options: { diff --git a/src/bin/help.test.ts b/src/bin/help.test.ts deleted file mode 100644 index 42f0965f2..000000000 --- a/src/bin/help.test.ts +++ /dev/null @@ -1,354 +0,0 @@ -import chalk from "chalk"; -import { beforeEach, describe, expect, it, MockInstance, vi } from "vitest"; - -import { logHelpText } from "./help.js"; - -function makeProxy(receiver: T): T { - return new Proxy(receiver, { - get: () => makeProxy((input: string) => input), - }); -} - -vi.mock("chalk", () => ({ - default: makeProxy({}), -})); - -let mockConsoleLog: MockInstance; - -describe("logHelpText", () => { - beforeEach(() => { - mockConsoleLog = vi - .spyOn(console, "log") - .mockImplementation(() => undefined); - }); - - it("logs help text when called", () => { - logHelpText([ - chalk.yellow( - "⚠️ This template is early stage, opinionated, and not endorsed by the TypeScript team. ⚠️", - ), - chalk.yellow( - "⚠️ If any tooling it sets displeases you, you can always remove that portion manually. ⚠️", - ), - ]); - - expect(mockConsoleLog.mock.calls).toMatchInlineSnapshot(` - [ - [ - "⚠️ This template is early stage, opinionated, and not endorsed by the TypeScript team. ⚠️", - ], - [ - " ", - ], - [ - "⚠️ If any tooling it sets displeases you, you can always remove that portion manually. ⚠️", - ], - [ - " ", - ], - [ - " - A quickstart-friendly TypeScript template with comprehensive formatting, - linting, releases, testing, and other great tooling built-in. - ", - ], - [ - " ", - ], - [ - "Core options:", - ], - [ - " - --base (string): Whether to scaffold the repository with: - • everything: that comes with the template (recommended) - • common: additions to the minimal starters such as releases and tests - • minimal: amounts of tooling, essentially opting out of everything - • prompt: for which portions to exclude", - ], - [ - " - --create-repository: Whether to create a corresponding repository on github.com - (if it doesn't yet exist)", - ], - [ - " - --description (string): Sentence case description of the repository - (e.g. A quickstart-friendly TypeScript package with lots of great - repository tooling. ✨)", - ], - [ - " - --mode (string): Whether to: - • create: a new repository in a child directory - • initialize: a freshly repository in the current directory - • migrate: an existing repository in the current directory", - ], - [ - " - --owner (string): GitHub organization or user the repository is underneath - (e.g. JoshuaKGoldberg)", - ], - [ - " - --repository (string): The kebab-case name of the repository - (e.g. create-typescript-app)", - ], - [ - " - --title (string): Title Case title for the repository to be used in - documentation (e.g. Create TypeScript App)", - ], - [], - [ - " ", - ], - [ - "Optional options:", - ], - [ - " - --access (string): ("public" | "restricted"): Which npm publish --access to - release npm packages with (by default, "public")", - ], - [ - " - --author (string): Username on npm to publish packages under (by - default, an existing npm author, or the currently logged in npm user, or - owner.toLowerCase())", - ], - [ - " - --auto: Whether to infer all options from files on disk.", - ], - [ - " - --bin (string): package.json bin value to include for npx-style running.", - ], - [ - " - --directory (string): Directory to create the repository in (by default, the same - name as the repository)", - ], - [ - " - --email (string): Email address to be listed as the point of contact in docs - and packages (e.g. example@joshuakgoldberg.com)", - ], - [ - " - --email-github (string): Optionally, may be provided to use different emails in .md - files", - ], - [ - " - --email-npm (string): Optionally, may be provided to use different emails in - package.json", - ], - [ - " - --funding (string): GitHub organization or username to mention in funding.yml - (by default, owner)", - ], - [ - " - --guide (string): Link to a contribution guide to place at the top of the - development docs", - ], - [ - " - --guide-title (string): If --guide is provided or detected from an existing - DEVELOPMENT.md, the text title to place in the guide link", - ], - [ - " - --keywords (string): Any number of keywords to include in package.json (by default, - none). This can be specified any number of times, like - --keywords apple --keywords "banana cherry"", - ], - [ - " - --logo (string): Local image file in the repository to display near the top of - the README.md as a logo", - ], - [ - " - --logo-alt (string): If --logo is provided or detected from an existing README.md, - alt text that describes the image will be prompted for if not provided", - ], - [ - " - --logo-height (string): If --logo is provided or detected from an existing README.md, - an explicit height style", - ], - [ - " - --logo-width (string): If --logo is provided or detected from an existing README.md, - an explicit width style", - ], - [ - " - --preserve-generated-from: Whether to keep the GitHub repository generated from - notice (by default, false)", - ], - [], - [ - " ", - ], - [ - "Opt-outs:", - ], - [ - " - ⚠️ Warning: Specifying any --exclude-* flag on the command-line will - cause the setup script to skip prompting for more excludes. ⚠️", - ], - [ - " - --exclude-all-contributors: Don't add all-contributors to track contributions - and display them in a README.md table.", - ], - [ - " - --exclude-compliance: Don't add a GitHub Actions workflow to verify that PRs match - an expected format.", - ], - [ - " - --exclude-lint-jsdoc: Don't use eslint-plugin-jsdoc to enforce good practices around - JSDoc comments.", - ], - [ - " - --exclude-lint-json: Don't apply linting and sorting to *.json, and - *.jsonc files.", - ], - [ - " - --exclude-lint-knip: Don't add Knip to detect unused files, dependencies, and code - exports.", - ], - [ - " - --exclude-lint-md: Don't apply linting to *.md files.", - ], - [ - " - --exclude-lint-package-json: Don't add eslint-plugin-package-json to lint for - package.json correctness.", - ], - [ - " - --exclude-lint-packages: Don't add a pnpm dedupe workflow to ensure packages - aren't duplicated unnecessarily.", - ], - [ - " - --exclude-lint-perfectionist: Don't apply eslint-plugin-perfectionist to ensure - imports, keys, and so on are in sorted order.", - ], - [ - " - --exclude-lint-regexp: Don't add eslint-plugin-regexp to enforce good practices around - regular expressions.", - ], - [ - " - --exclude-lint-spelling: Don't add cspell to spell check against dictionaries - of known words.", - ], - [ - " - --exclude-lint-strict: Don't augment the recommended logical lint rules with - typescript-eslint's strict config.", - ], - [ - " - --exclude-lint-stylistic: Don't add stylistic rules such as typescript-eslint's - stylistic config.", - ], - [ - " - --exclude-lint-yml: Don't apply linting and sorting to *.yaml and *.yml files.", - ], - [ - " - --exclude-releases: Don't add release-it to generate changelogs, package bumps, - and publishes based on conventional commits.", - ], - [ - " - --exclude-renovate: Don't add a Renovate config to dependencies up-to-date with - PRs.", - ], - [ - " - --exclude-templated-by: Don't add a _"This package was templated with create-typescript-app"_ notice at the end of the README.md.", - ], - [ - " - --exclude-tests: Don't add Vitest tooling for fast unit tests, configured - with coverage tracking.", - ], - [ - " - You can prevent the migration script from making some network-based - changes using any or all of the following CLI flags:", - ], - [ - " - --exclude-contributors: Skips network calls that fetch all-contributors - data from GitHub", - ], - [ - " - --skip-all-contributors-api: Skips network calls that fetch all-contributors data from - GitHub. This flag does nothing if --exclude-all-contributors was specified.", - ], - [ - " - --skip-github-api: Skips calling to GitHub APIs.", - ], - [ - " - --skip-install: Skips installing all the new template packages with pnpm.", - ], - [ - " - You can prevent the migration script from making some changes on disk - using any or all of the following CLI flags:", - ], - [ - " - --skip-removal: Skips removing setup docs and scripts, including this docs/ - directory", - ], - [ - " - --skip-restore: Skips the prompt offering to restore the repository if an - error occurs during setup", - ], - [ - " - --skip-uninstall: Skips uninstalling packages only used for setup scripts", - ], - [], - [ - " ", - ], - [ - "Offline Mode:", - ], - [ - " - --offline: You can run create-typescript-app in an "offline" mode. - Doing so will: - • Enable --exclude-all-contributors-api and --skip-github-api - • Skip network calls when setting up contributors - • Run pnpm commands with pnpm's --offline mode", - ], - [], - ] - `); - }); -}); diff --git a/src/bin/help.ts b/src/bin/help.ts deleted file mode 100644 index a36b8737a..000000000 --- a/src/bin/help.ts +++ /dev/null @@ -1,148 +0,0 @@ -import chalk from "chalk"; - -import { allArgOptions } from "../shared/options/args.js"; - -interface HelpTextSection { - sectionHeading: string; - subsections: { - flags: SubsectionFlag[]; - subheading?: string; - warning?: string; - }[]; -} - -interface SubsectionFlag { - description: string; - flag: string; - type: string; -} - -export function logHelpText(introLogs: string[]): void { - const helpTextSections = createHelpTextSections(); - - for (const log of introLogs) { - console.log(log); - console.log(" "); - } - - console.log( - chalk.cyan( - ` -A quickstart-friendly TypeScript template with comprehensive formatting, -linting, releases, testing, and other great tooling built-in. - `, - ), - ); - - for (const section of helpTextSections) { - logHelpTextSection(section); - - console.log(); - } -} - -function createHelpTextSections(): HelpTextSection[] { - const core: HelpTextSection = { - sectionHeading: "Core options:", - subsections: [ - { - flags: [], - }, - ], - }; - - const optional: HelpTextSection = { - sectionHeading: "Optional options:", - subsections: [ - { - flags: [], - }, - ], - }; - - const optOut: HelpTextSection = { - sectionHeading: "Opt-outs:", - subsections: [ - { - flags: [], - warning: ` - ⚠️ Warning: Specifying any --exclude-* flag on the command-line will - cause the setup script to skip prompting for more excludes. ⚠️`, - }, - { - flags: [ - { - description: `Skips network calls that fetch all-contributors - data from GitHub`, - flag: "exclude-contributors", - type: "boolean", - }, - ], - subheading: ` -You can prevent the migration script from making some network-based -changes using any or all of the following CLI flags:`, - }, - { - flags: [], - subheading: ` -You can prevent the migration script from making some changes on disk -using any or all of the following CLI flags:`, - }, - ], - }; - - const offline: HelpTextSection = { - sectionHeading: "Offline Mode:", - subsections: [ - { - flags: [], - }, - ], - }; - - const subsections = { - core: core.subsections[0], - offline: offline.subsections[0], - "opt-out": optOut.subsections[0], - optional: optional.subsections[0], - "skip-disk": optOut.subsections[2], - "skip-net": optOut.subsections[1], - }; - - for (const [option, data] of Object.entries(allArgOptions)) { - subsections[data.docsSection].flags.push({ - description: data.description, - flag: option, - type: data.type, - }); - } - - return [core, optional, optOut, offline]; -} - -function logHelpTextSection(section: HelpTextSection): void { - console.log(" "); - - console.log(chalk.black.bgGreenBright(section.sectionHeading)); - - for (const subsection of section.subsections) { - if (subsection.warning) { - console.log(chalk.yellow(subsection.warning)); - } - - if (subsection.subheading) { - console.log(chalk.green(subsection.subheading)); - } - - for (const { description, flag, type } of subsection.flags) { - console.log( - chalk.cyan( - ` - --${flag}${ - type !== "boolean" ? ` (${chalk.cyanBright(type)})` : "" - }: ${description}`, - ), - ); - } - } -} diff --git a/src/bin/index.test.ts b/src/bin/index.test.ts deleted file mode 100644 index 8ed7fa0a9..000000000 --- a/src/bin/index.test.ts +++ /dev/null @@ -1,205 +0,0 @@ -import chalk from "chalk"; -import { beforeEach, describe, expect, it, vi } from "vitest"; -import z from "zod"; - -import { bin } from "./index.js"; -import { getVersionFromPackageJson } from "./packageJson.js"; - -const mockCancel = vi.fn(); -const mockOutro = vi.fn(); - -vi.mock("@clack/prompts", () => ({ - get cancel() { - return mockCancel; - }, - intro: vi.fn(), - log: { - info: vi.fn(), - }, - get outro() { - return mockOutro; - }, -})); - -const mockLogLine = vi.fn(); - -vi.mock("../shared/cli/lines.js", () => ({ - get logLine() { - return mockLogLine; - }, -})); - -const mockCreate = vi.fn(); - -vi.mock("../create/index.js", () => ({ - get create() { - return mockCreate; - }, -})); - -const mockInitialize = vi.fn(); - -vi.mock("../initialize/index.js", () => ({ - get initialize() { - return mockInitialize; - }, -})); - -const mockMigrate = vi.fn(); - -vi.mock("../migrate/index.js", () => ({ - get migrate() { - return mockMigrate; - }, -})); - -const mockPromptForMode = vi.fn(); - -vi.mock("./promptForMode.js", () => ({ - get promptForMode() { - return mockPromptForMode; - }, -})); - -describe("bin", () => { - beforeEach(() => { - vi.spyOn(console, "clear").mockImplementation(() => undefined); - vi.spyOn(console, "log").mockImplementation(() => undefined); - }); - - it("returns 1 when promptForMode returns an undefined mode", async () => { - mockPromptForMode.mockResolvedValue({}); - - const result = await bin([]); - - expect(mockOutro).toHaveBeenCalledWith( - chalk.red("Operation cancelled. Exiting - maybe another time? 👋"), - ); - expect(result).toBe(1); - }); - - it("returns 1 when promptForMode returns an error mode", async () => { - const error = new Error("Oh no!"); - mockPromptForMode.mockResolvedValue({ mode: error }); - - const result = await bin([]); - - expect(mockOutro).toHaveBeenCalledWith(chalk.red(error.message)); - expect(result).toBe(1); - }); - - it("returns the success result of the corresponding runner without cancel logging when promptForMode returns a mode that succeeds", async () => { - const mode = "create"; - const args = ["--owner", "abc123"]; - const code = 0; - const promptedOptions = { directory: "." }; - - mockPromptForMode.mockResolvedValue({ mode, options: promptedOptions }); - mockCreate.mockResolvedValue({ code, options: {} }); - - const result = await bin(args); - - expect(mockCreate).toHaveBeenCalledWith(args, promptedOptions); - expect(mockCancel).not.toHaveBeenCalled(); - expect(mockInitialize).not.toHaveBeenCalled(); - expect(mockMigrate).not.toHaveBeenCalled(); - expect(result).toEqual(code); - }); - - it("returns the cancel result of the corresponding runner and cancel logs when promptForMode returns a mode that cancels", async () => { - const mode = "create"; - const args = ["--owner", "abc123"]; - const code = 2; - const promptedOptions = { directory: "." }; - - mockPromptForMode.mockResolvedValue({ mode, options: promptedOptions }); - mockCreate.mockResolvedValue({ code, options: {} }); - - const result = await bin(args); - - expect(mockCreate).toHaveBeenCalledWith(args, promptedOptions); - expect(mockCancel).toHaveBeenCalledWith( - `Operation cancelled. Exiting - maybe another time? 👋`, - ); - expect(result).toEqual(code); - }); - - it("returns the cancel result containing a zod error of the corresponding runner and output plus cancel logs when promptForMode returns a mode that cancels with a string error", async () => { - const mode = "initialize"; - const args = ["--email", "abc123"]; - const code = 2; - const error = "Oh no!"; - - mockPromptForMode.mockResolvedValue({ mode }); - mockInitialize.mockResolvedValue({ - code: 2, - error, - options: {}, - }); - - const result = await bin(args); - - expect(mockInitialize).toHaveBeenCalledWith(args, undefined); - expect(mockLogLine).toHaveBeenCalledWith(chalk.red(error)); - expect(mockCancel).toHaveBeenCalledWith( - `Operation cancelled. Exiting - maybe another time? 👋`, - ); - expect(result).toEqual(code); - }); - - it("returns the cancel result containing a zod error of the corresponding runner and output plus cancel logs when promptForMode returns a mode that cancels with a zod error", async () => { - const mode = "initialize"; - const args = ["--email", "abc123"]; - const code = 2; - - const validationResult = z - .object({ email: z.string().email() }) - .safeParse({ email: "abc123" }); - - mockPromptForMode.mockResolvedValue({ mode }); - mockInitialize.mockResolvedValue({ - code: 2, - error: (validationResult as z.SafeParseError<{ email: string }>).error, - options: {}, - }); - - const result = await bin(args); - - expect(mockInitialize).toHaveBeenCalledWith(args, undefined); - expect(mockLogLine).toHaveBeenCalledWith( - chalk.red('Validation error: Invalid email at "email"'), - ); - expect(mockCancel).toHaveBeenCalledWith( - `Operation cancelled. Exiting - maybe another time? 👋`, - ); - expect(result).toEqual(code); - }); - - it("returns the cancel result of the corresponding runner and cancel logs when promptForMode returns a mode that fails", async () => { - const mode = "create"; - const args = ["--owner", "abc123"]; - const code = 1; - const promptedOptions = { directory: "." }; - - mockPromptForMode.mockResolvedValue({ mode, options: promptedOptions }); - mockCreate.mockResolvedValue({ code, options: {} }); - - const result = await bin(args); - - expect(mockCreate).toHaveBeenCalledWith(args, promptedOptions); - expect(mockCancel).toHaveBeenCalledWith( - `Operation failed. Exiting - maybe another time? 👋`, - ); - expect(result).toEqual(code); - }); - - it("prints the version when the --version flag is passed", async () => { - const args = ["--version"]; - const version = await getVersionFromPackageJson(); - - const result = await bin(args); - - expect(console.log).toHaveBeenCalledWith(version); - expect(result).toBe(0); - }); -}); diff --git a/src/bin/index.ts b/src/bin/index.ts deleted file mode 100644 index 923727759..000000000 --- a/src/bin/index.ts +++ /dev/null @@ -1,108 +0,0 @@ -import * as prompts from "@clack/prompts"; -import chalk from "chalk"; -import { parseArgs } from "node:util"; -import { fromZodError } from "zod-validation-error"; - -import { createRerunSuggestion } from "../create/createRerunSuggestion.js"; -import { create } from "../create/index.js"; -import { initialize } from "../initialize/index.js"; -import { migrate } from "../migrate/index.js"; -import { logLine } from "../shared/cli/lines.js"; -import { StatusCodes } from "../shared/codes.js"; -import { logHelpText } from "./help.js"; -import { getVersionFromPackageJson } from "./packageJson.js"; -import { promptForMode } from "./promptForMode.js"; - -const operationMessage = (verb: string) => - `Operation ${verb}. Exiting - maybe another time? 👋`; - -export async function bin(args: string[]) { - console.clear(); - - const version = await getVersionFromPackageJson(); - - const introPrompts = [ - chalk.greenBright(`✨ Welcome to`), - chalk.bgGreenBright.black(`create-typescript-app`), - chalk.greenBright(`${version}! ✨`), - ].join(" "); - - const introWarnings = [ - chalk.yellow( - "⚠️ This template is early stage, opinionated, and not endorsed by the TypeScript team. ⚠️", - ), - chalk.yellow( - "⚠️ If any tooling it sets displeases you, you can always remove that portion manually. ⚠️", - ), - ]; - - const { values } = parseArgs({ - args, - options: { - help: { - short: "h", - type: "boolean", - }, - mode: { type: "string" }, - version: { - short: "v", - type: "boolean", - }, - }, - strict: false, - }); - - if (values.help) { - logHelpText([introPrompts, ...introWarnings]); - return 0; - } - - if (values.version) { - console.log(version); - return 0; - } - - prompts.intro(introPrompts); - - logLine(); - logLine(introWarnings[0]); - logLine(introWarnings[1]); - - const { mode, options: promptedOptions } = await promptForMode( - !!values.auto, - values.mode, - ); - if (typeof mode !== "string") { - prompts.outro(chalk.red(mode?.message ?? operationMessage("cancelled"))); - return 1; - } - - const runners = { create, initialize, migrate }; - const { code, error, options } = await runners[mode](args, promptedOptions); - - prompts.log.info( - [ - chalk.italic(`Tip: to run again with the same input values, use:`), - chalk.blue(createRerunSuggestion(options)), - ].join(" "), - ); - - if (code) { - logLine(); - - if (error) { - logLine( - chalk.red(typeof error === "string" ? error : fromZodError(error)), - ); - logLine(); - } - - prompts.cancel( - code === StatusCodes.Cancelled - ? operationMessage("cancelled") - : operationMessage("failed"), - ); - } - - return code; -} diff --git a/src/bin/packageJson.test.ts b/src/bin/packageJson.test.ts deleted file mode 100644 index 7e1be8dee..000000000 --- a/src/bin/packageJson.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; - -import { getVersionFromPackageJson } from "./packageJson.js"; - -const mockReadFileSafeAsJson = vi.fn(); - -vi.mock("../shared/readFileSafeAsJson.js", () => ({ - get readFileSafeAsJson() { - return mockReadFileSafeAsJson; - }, -})); - -describe("getVersionFromPackageJson", () => { - it("returns the current version number from the package.json", async () => { - mockReadFileSafeAsJson.mockResolvedValue({ - version: "1.40.0", - }); - - const version = await getVersionFromPackageJson(); - - expect(version).toBe("1.40.0"); - }); - - it("throws an error when there is no version number", async () => { - mockReadFileSafeAsJson.mockResolvedValue({}); - - await expect(() => getVersionFromPackageJson()).rejects.toEqual( - new Error("Cannot find version number"), - ); - }); -}); diff --git a/src/bin/packageJson.ts b/src/bin/packageJson.ts deleted file mode 100644 index 18d7004e2..000000000 --- a/src/bin/packageJson.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { readFileSafeAsJson } from "../shared/readFileSafeAsJson.js"; - -interface PackageWithVersion { - version?: string; -} - -export async function getVersionFromPackageJson(): Promise { - const path = new URL("../../package.json", import.meta.url); - const data = (await readFileSafeAsJson(path)) as PackageWithVersion; - - if (typeof data === "object" && typeof data.version === "string") { - return data.version; - } - - throw new Error("Cannot find version number"); -} diff --git a/src/bin/promptForMode.test.ts b/src/bin/promptForMode.test.ts deleted file mode 100644 index f508c6d14..000000000 --- a/src/bin/promptForMode.test.ts +++ /dev/null @@ -1,129 +0,0 @@ -import chalk from "chalk"; -import { describe, expect, it, vi } from "vitest"; - -import { promptForMode } from "./promptForMode.js"; - -const mockSelect = vi.fn(); - -vi.mock("@clack/prompts", () => ({ - isCancel: () => false, - get select() { - return mockSelect; - }, -})); - -const mockReaddir = vi.fn(); - -vi.mock("node:fs/promises", () => ({ - get readdir() { - return mockReaddir; - }, -})); - -const mockCwd = vi.fn(); - -vi.mock("node:process", () => ({ - get cwd() { - return mockCwd; - }, -})); - -const mockLogLine = vi.fn(); - -vi.mock("../shared/cli/lines.js", () => ({ - get logLine() { - return mockLogLine; - }, -})); -describe("promptForMode", () => { - it("returns an error when auto exists and input is not migrate", async () => { - const mode = await promptForMode(true, "create"); - - expect(mode).toMatchInlineSnapshot( - ` - { - "mode": [Error: --auto can only be used with --mode migrate.], - } - `, - ); - }); - - it("returns an error when the input exists and is not a mode", async () => { - const mode = await promptForMode(false, "other"); - - expect(mode).toMatchInlineSnapshot( - ` - { - "mode": [Error: Unknown --mode: other. Allowed modes are: create, initialize, migrate.], - } - `, - ); - }); - - it("returns the input when it is a mode", async () => { - const input = "create"; - - const mode = await promptForMode(false, input); - - expect(mode).toEqual({ mode: input }); - }); - - it("returns creating in the current directory when the current directory is empty and the user selects create-current", async () => { - mockSelect.mockResolvedValueOnce("create-current"); - const directory = "test-directory"; - - mockReaddir.mockResolvedValueOnce([]); - mockCwd.mockReturnValueOnce(`/path/to/${directory}`); - - const actual = await promptForMode(false, undefined); - - expect(actual).toEqual({ - mode: "create", - options: { directory: ".", repository: directory }, - }); - expect(mockLogLine).not.toHaveBeenCalled(); - }); - - it("returns creating in a child directory when the current directory is empty and the user selects create-child", async () => { - mockSelect.mockResolvedValueOnce("create-child"); - const directory = "test-directory"; - - mockReaddir.mockResolvedValueOnce([]); - mockCwd.mockReturnValueOnce(`/path/to/${directory}`); - - const actual = await promptForMode(false, undefined); - - expect(actual).toEqual({ - mode: "create", - }); - expect(mockLogLine).not.toHaveBeenCalled(); - }); - - it("returns the user selection when the current directory is a Git directory", async () => { - const mode = "initialize"; - mockSelect.mockResolvedValueOnce(mode); - - mockReaddir.mockResolvedValueOnce([".git"]); - - const actual = await promptForMode(false, undefined); - - expect(actual).toEqual({ mode }); - expect(mockLogLine).not.toHaveBeenCalled(); - }); - - it("returns create without prompting when the current directory contains children but is not a Git directory", async () => { - const mode = "create"; - - mockReaddir.mockResolvedValueOnce(["file"]); - - const actual = await promptForMode(false, undefined); - - expect(actual).toEqual({ mode }); - expect(mockSelect).not.toHaveBeenCalled(); - expect(mockLogLine).toHaveBeenCalledWith( - chalk.gray( - "Defaulting to --mode create because the directory contains children and isn't a Git repository.", - ), - ); - }); -}); diff --git a/src/bin/promptForMode.ts b/src/bin/promptForMode.ts deleted file mode 100644 index 8bb6ab43c..000000000 --- a/src/bin/promptForMode.ts +++ /dev/null @@ -1,127 +0,0 @@ -import * as prompts from "@clack/prompts"; -import chalk from "chalk"; -import * as fs from "fs/promises"; -import path from "node:path"; -import * as process from "node:process"; - -import { logLine } from "../shared/cli/lines.js"; -import { isUsingCreateEngine } from "../shared/isUsingCreateEngine.js"; -import { filterPromptCancel } from "../shared/prompts.js"; -import { Mode, PromptedOptions } from "../shared/types.js"; - -const allowedModes = ["create", "initialize", "migrate"] satisfies Mode[]; - -export interface PromptedMode { - mode: Error | Mode | undefined; - options?: PromptedOptions; -} - -export async function promptForMode( - auto: boolean, - input: boolean | string | undefined, -): Promise { - if (auto && input !== "migrate") { - return { - mode: new Error("--auto can only be used with --mode migrate."), - }; - } - - if (input) { - if (!isMode(input)) { - return { - mode: new Error( - `Unknown --mode: ${input}. Allowed modes are: ${allowedModes.join( - ", ", - )}.`, - ), - }; - } - - return { mode: input }; - } - - const dir = await fs.readdir("."); - - if (dir.length === 0 && !isUsingCreateEngine()) { - const mode = filterPromptCancel( - (await prompts.select({ - message: chalk.blue("How would you like to use the template?"), - options: [ - { - label: label( - "create", - "a new repository in the current empty directory", - ), - value: "create-current", - }, - { - label: label("create", "a new repository in a new child directory"), - value: "create-child", - }, - ], - })) as string, - ); - - const directory = path.basename(process.cwd()); - - return { - mode: "create", - ...(mode === "create-current" && { - options: { - directory: ".", - repository: directory, - }, - }), - }; - } - - if (dir.includes(".git")) { - return { - mode: filterPromptCancel( - (await prompts.select({ - initialValue: "migrate" as Mode, - message: chalk.blue("How would you like to use the template?"), - options: [ - { - label: label("create", "a new repository in a child directory"), - value: "create", - }, - { - label: label( - "initialize", - "a freshly cloned repository in the current directory", - ), - value: "initialize", - }, - { - label: label( - "migrate", - "the existing repository in the current directory", - ), - value: "migrate", - }, - ], - })) as Mode | symbol, - ), - }; - } - - logLine(); - logLine( - chalk.gray( - "Defaulting to --mode create because the directory contains children and isn't a Git repository.", - ), - ); - - return { - mode: "create", - }; -} - -function isMode(input: boolean | string): input is Mode { - return allowedModes.includes(input as Mode); -} - -function label(base: string, text: string) { - return `${chalk.bold(base)} ${text}`; -} diff --git a/src/next/blocks/blockAllContributors.test.ts b/src/blocks/blockAllContributors.test.ts similarity index 100% rename from src/next/blocks/blockAllContributors.test.ts rename to src/blocks/blockAllContributors.test.ts diff --git a/src/next/blocks/blockAllContributors.ts b/src/blocks/blockAllContributors.ts similarity index 94% rename from src/next/blocks/blockAllContributors.ts rename to src/blocks/blockAllContributors.ts index 1ecd3f85d..0c91d3b0b 100644 --- a/src/next/blocks/blockAllContributors.ts +++ b/src/blocks/blockAllContributors.ts @@ -1,6 +1,6 @@ -import { createSoloWorkflowFile } from "../../steps/writing/creation/dotGitHub/createSoloWorkflowFile.js"; import { base } from "../base.js"; import { blockRepositorySecrets } from "./blockRepositorySecrets.js"; +import { createSoloWorkflowFile } from "./files/createSoloWorkflowFile.js"; import { CommandPhase } from "./phases.js"; export const blockAllContributors = base.createBlock({ diff --git a/src/next/blocks/blockAreTheTypesWrong.test.ts b/src/blocks/blockAreTheTypesWrong.test.ts similarity index 100% rename from src/next/blocks/blockAreTheTypesWrong.test.ts rename to src/blocks/blockAreTheTypesWrong.test.ts diff --git a/src/next/blocks/blockAreTheTypesWrong.ts b/src/blocks/blockAreTheTypesWrong.ts similarity index 93% rename from src/next/blocks/blockAreTheTypesWrong.ts rename to src/blocks/blockAreTheTypesWrong.ts index 79b32105d..dfe35c49e 100644 --- a/src/next/blocks/blockAreTheTypesWrong.ts +++ b/src/blocks/blockAreTheTypesWrong.ts @@ -3,7 +3,7 @@ import { blockGitHubActionsCI } from "./blockGitHubActionsCI.js"; export const blockAreTheTypesWrong = base.createBlock({ about: { - name: "README.md", + name: "Are The Types Wrong", }, produce() { return { diff --git a/src/next/blocks/blockCSpell.test.ts b/src/blocks/blockCSpell.test.ts similarity index 100% rename from src/next/blocks/blockCSpell.test.ts rename to src/blocks/blockCSpell.test.ts diff --git a/src/next/blocks/blockCSpell.ts b/src/blocks/blockCSpell.ts similarity index 97% rename from src/next/blocks/blockCSpell.ts rename to src/blocks/blockCSpell.ts index 130991124..5a3cebb14 100644 --- a/src/next/blocks/blockCSpell.ts +++ b/src/blocks/blockCSpell.ts @@ -2,12 +2,12 @@ import { getObjectStringsDeep } from "object-strings-deep"; import { z } from "zod"; import { base } from "../base.js"; +import { getPackageDependencies } from "../data/packageData.js"; import { resolveBin } from "../utils/resolveBin.js"; import { blockDevelopmentDocs } from "./blockDevelopmentDocs.js"; import { blockGitHubActionsCI } from "./blockGitHubActionsCI.js"; import { blockPackageJson } from "./blockPackageJson.js"; import { blockVSCode } from "./blockVSCode.js"; -import { getPackageDependencies } from "./packageData.js"; import { CommandPhase } from "./phases.js"; const filesGlob = `"**" ".github/**/*"`; diff --git a/src/next/blocks/blockContributingDocs.test.ts b/src/blocks/blockContributingDocs.test.ts similarity index 100% rename from src/next/blocks/blockContributingDocs.test.ts rename to src/blocks/blockContributingDocs.test.ts diff --git a/src/next/blocks/blockContributingDocs.ts b/src/blocks/blockContributingDocs.ts similarity index 100% rename from src/next/blocks/blockContributingDocs.ts rename to src/blocks/blockContributingDocs.ts diff --git a/src/next/blocks/blockContributorCovenant.test.ts b/src/blocks/blockContributorCovenant.test.ts similarity index 100% rename from src/next/blocks/blockContributorCovenant.test.ts rename to src/blocks/blockContributorCovenant.test.ts diff --git a/src/next/blocks/blockContributorCovenant.ts b/src/blocks/blockContributorCovenant.ts similarity index 100% rename from src/next/blocks/blockContributorCovenant.ts rename to src/blocks/blockContributorCovenant.ts diff --git a/src/next/blocks/blockDevelopmentDocs.test.ts b/src/blocks/blockDevelopmentDocs.test.ts similarity index 100% rename from src/next/blocks/blockDevelopmentDocs.test.ts rename to src/blocks/blockDevelopmentDocs.test.ts diff --git a/src/next/blocks/blockDevelopmentDocs.ts b/src/blocks/blockDevelopmentDocs.ts similarity index 100% rename from src/next/blocks/blockDevelopmentDocs.ts rename to src/blocks/blockDevelopmentDocs.ts diff --git a/src/next/blocks/blockESLint.test.ts b/src/blocks/blockESLint.test.ts similarity index 98% rename from src/next/blocks/blockESLint.test.ts rename to src/blocks/blockESLint.test.ts index 9fce1268e..4c24fa1b5 100644 --- a/src/next/blocks/blockESLint.test.ts +++ b/src/blocks/blockESLint.test.ts @@ -71,7 +71,7 @@ describe("blockESLint", () => { "@eslint/js": "9.17.0", "@types/node": "22.10.2", "eslint": "9.17.0", - "typescript-eslint": "8.18.1", + "typescript-eslint": "8.19.0", }, "scripts": { "lint": "eslint . --max-warnings 0", @@ -200,7 +200,7 @@ describe("blockESLint", () => { "@eslint/js": "9.17.0", "@types/node": "22.10.2", "eslint": "9.17.0", - "typescript-eslint": "8.18.1", + "typescript-eslint": "8.19.0", }, "scripts": { "lint": "eslint . --max-warnings 0", @@ -366,7 +366,7 @@ describe("blockESLint", () => { "eslint": "9.17.0", "eslint-plugin-markdown": "5.1.0", "eslint-plugin-regexp": "2.7.0", - "typescript-eslint": "8.18.1", + "typescript-eslint": "8.19.0", }, "scripts": { "lint": "eslint . --max-warnings 0", diff --git a/src/next/blocks/blockESLint.ts b/src/blocks/blockESLint.ts similarity index 99% rename from src/next/blocks/blockESLint.ts rename to src/blocks/blockESLint.ts index cbbf38ffd..c4280bea6 100644 --- a/src/next/blocks/blockESLint.ts +++ b/src/blocks/blockESLint.ts @@ -3,12 +3,12 @@ import { parse as parsePackageName } from "parse-package-name"; import { z } from "zod"; import { base } from "../base.js"; +import { getPackageDependencies } from "../data/packageData.js"; import { blockCSpell } from "./blockCSpell.js"; import { blockDevelopmentDocs } from "./blockDevelopmentDocs.js"; import { blockGitHubActionsCI } from "./blockGitHubActionsCI.js"; import { blockPackageJson } from "./blockPackageJson.js"; import { blockVSCode } from "./blockVSCode.js"; -import { getPackageDependencies } from "./packageData.js"; import { CommandPhase } from "./phases.js"; const zRuleOptions = z.union([ diff --git a/src/next/blocks/blockESLintComments.ts b/src/blocks/blockESLintComments.ts similarity index 100% rename from src/next/blocks/blockESLintComments.ts rename to src/blocks/blockESLintComments.ts diff --git a/src/next/blocks/blockESLintJSDoc.ts b/src/blocks/blockESLintJSDoc.ts similarity index 100% rename from src/next/blocks/blockESLintJSDoc.ts rename to src/blocks/blockESLintJSDoc.ts diff --git a/src/next/blocks/blockESLintJSONC.ts b/src/blocks/blockESLintJSONC.ts similarity index 100% rename from src/next/blocks/blockESLintJSONC.ts rename to src/blocks/blockESLintJSONC.ts diff --git a/src/next/blocks/blockESLintMarkdown.ts b/src/blocks/blockESLintMarkdown.ts similarity index 100% rename from src/next/blocks/blockESLintMarkdown.ts rename to src/blocks/blockESLintMarkdown.ts diff --git a/src/next/blocks/blockESLintMoreStyling.ts b/src/blocks/blockESLintMoreStyling.ts similarity index 100% rename from src/next/blocks/blockESLintMoreStyling.ts rename to src/blocks/blockESLintMoreStyling.ts diff --git a/src/next/blocks/blockESLintNode.ts b/src/blocks/blockESLintNode.ts similarity index 100% rename from src/next/blocks/blockESLintNode.ts rename to src/blocks/blockESLintNode.ts diff --git a/src/next/blocks/blockESLintPackageJson.ts b/src/blocks/blockESLintPackageJson.ts similarity index 100% rename from src/next/blocks/blockESLintPackageJson.ts rename to src/blocks/blockESLintPackageJson.ts diff --git a/src/next/blocks/blockESLintPerfectionist.ts b/src/blocks/blockESLintPerfectionist.ts similarity index 100% rename from src/next/blocks/blockESLintPerfectionist.ts rename to src/blocks/blockESLintPerfectionist.ts diff --git a/src/next/blocks/blockESLintRegexp.ts b/src/blocks/blockESLintRegexp.ts similarity index 100% rename from src/next/blocks/blockESLintRegexp.ts rename to src/blocks/blockESLintRegexp.ts diff --git a/src/next/blocks/blockESLintYML.ts b/src/blocks/blockESLintYML.ts similarity index 100% rename from src/next/blocks/blockESLintYML.ts rename to src/blocks/blockESLintYML.ts diff --git a/src/next/blocks/blockExampleFiles.test.ts b/src/blocks/blockExampleFiles.test.ts similarity index 100% rename from src/next/blocks/blockExampleFiles.test.ts rename to src/blocks/blockExampleFiles.test.ts diff --git a/src/next/blocks/blockExampleFiles.ts b/src/blocks/blockExampleFiles.ts similarity index 100% rename from src/next/blocks/blockExampleFiles.ts rename to src/blocks/blockExampleFiles.ts diff --git a/src/next/blocks/blockFunding.ts b/src/blocks/blockFunding.ts similarity index 77% rename from src/next/blocks/blockFunding.ts rename to src/blocks/blockFunding.ts index 934d3ed19..64cff2929 100644 --- a/src/next/blocks/blockFunding.ts +++ b/src/blocks/blockFunding.ts @@ -1,5 +1,5 @@ -import { formatYaml } from "../../steps/writing/creation/formatters/formatYaml.js"; import { base } from "../base.js"; +import { formatYaml } from "./files/formatYaml.js"; export const blockFunding = base.createBlock({ about: { diff --git a/src/next/blocks/blockGitHubActionsCI.test.ts b/src/blocks/blockGitHubActionsCI.test.ts similarity index 96% rename from src/next/blocks/blockGitHubActionsCI.test.ts rename to src/blocks/blockGitHubActionsCI.test.ts index af9efb099..1cc55e004 100644 --- a/src/next/blocks/blockGitHubActionsCI.test.ts +++ b/src/blocks/blockGitHubActionsCI.test.ts @@ -70,7 +70,7 @@ describe("blockGitHubActionsCI", () => { steps: - uses: actions-ecosystem/action-remove-labels@v1 with: - labels: "status: waiting for author" + labels: 'status: waiting for author' - if: failure() run: | echo "Don't worry if the previous step failed." @@ -159,7 +159,7 @@ describe("blockGitHubActionsCI", () => { steps: - uses: actions-ecosystem/action-remove-labels@v1 with: - labels: "status: waiting for author" + labels: 'status: waiting for author' - if: failure() run: | echo "Don't worry if the previous step failed." @@ -271,11 +271,11 @@ describe("blockGitHubActionsCI", () => { - uses: actions/checkout@v4 - uses: ./.github/actions/prepare - env: - VAR_ENV: "true" + VAR_ENV: 'true' if: always() run: pnpm validate with: - VAR_WITH: "true" + VAR_WITH: 'true' name: CI @@ -291,7 +291,7 @@ describe("blockGitHubActionsCI", () => { steps: - uses: actions-ecosystem/action-remove-labels@v1 with: - labels: "status: waiting for author" + labels: 'status: waiting for author' - if: failure() run: | echo "Don't worry if the previous step failed." diff --git a/src/next/blocks/blockGitHubActionsCI.ts b/src/blocks/blockGitHubActionsCI.ts similarity index 93% rename from src/next/blocks/blockGitHubActionsCI.ts rename to src/blocks/blockGitHubActionsCI.ts index ad5f9d6e7..b7c5ca522 100644 --- a/src/next/blocks/blockGitHubActionsCI.ts +++ b/src/blocks/blockGitHubActionsCI.ts @@ -1,9 +1,9 @@ import jsYaml from "js-yaml"; import { z } from "zod"; -import { createMultiWorkflowFile } from "../../steps/writing/creation/dotGitHub/createMultiWorkflowFile.js"; -import { createSoloWorkflowFile } from "../../steps/writing/creation/dotGitHub/createSoloWorkflowFile.js"; import { base } from "../base.js"; +import { createMultiWorkflowFile } from "./files/createMultiWorkflowFile.js"; +import { createSoloWorkflowFile } from "./files/createSoloWorkflowFile.js"; import { CommandPhase } from "./phases.js"; export const blockGitHubActionsCI = base.createBlock({ diff --git a/src/next/blocks/blockGitHubApps.ts b/src/blocks/blockGitHubApps.ts similarity index 100% rename from src/next/blocks/blockGitHubApps.ts rename to src/blocks/blockGitHubApps.ts diff --git a/src/next/blocks/blockGitHubIssueTemplates.ts b/src/blocks/blockGitHubIssueTemplates.ts similarity index 98% rename from src/next/blocks/blockGitHubIssueTemplates.ts rename to src/blocks/blockGitHubIssueTemplates.ts index 23bd3e977..6d3ef2bb3 100644 --- a/src/next/blocks/blockGitHubIssueTemplates.ts +++ b/src/blocks/blockGitHubIssueTemplates.ts @@ -1,5 +1,5 @@ -import { formatYaml } from "../../steps/writing/creation/formatters/formatYaml.js"; import { base } from "../base.js"; +import { formatYaml } from "./files/formatYaml.js"; export const blockGitHubIssueTemplates = base.createBlock({ about: { diff --git a/src/next/blocks/blockGitHubPRTemplate.ts b/src/blocks/blockGitHubPRTemplate.ts similarity index 100% rename from src/next/blocks/blockGitHubPRTemplate.ts rename to src/blocks/blockGitHubPRTemplate.ts diff --git a/src/next/blocks/blockGitignore.test.ts b/src/blocks/blockGitignore.test.ts similarity index 100% rename from src/next/blocks/blockGitignore.test.ts rename to src/blocks/blockGitignore.test.ts diff --git a/src/next/blocks/blockGitignore.ts b/src/blocks/blockGitignore.ts similarity index 79% rename from src/next/blocks/blockGitignore.ts rename to src/blocks/blockGitignore.ts index b10e4bf6c..fe607e3ef 100644 --- a/src/next/blocks/blockGitignore.ts +++ b/src/blocks/blockGitignore.ts @@ -1,7 +1,7 @@ import { z } from "zod"; -import { formatIgnoreFile } from "../../steps/writing/creation/formatters/formatIgnoreFile.js"; import { base } from "../base.js"; +import { formatIgnoreFile } from "./files/formatIgnoreFile.js"; export const blockGitignore = base.createBlock({ about: { diff --git a/src/next/blocks/blockKnip.ts b/src/blocks/blockKnip.ts similarity index 93% rename from src/next/blocks/blockKnip.ts rename to src/blocks/blockKnip.ts index fadbb2021..1883a6431 100644 --- a/src/next/blocks/blockKnip.ts +++ b/src/blocks/blockKnip.ts @@ -1,8 +1,11 @@ import { base } from "../base.js"; +import { + getPackageDependencies, + getPackageDependency, +} from "../data/packageData.js"; import { blockDevelopmentDocs } from "./blockDevelopmentDocs.js"; import { blockGitHubActionsCI } from "./blockGitHubActionsCI.js"; import { blockPackageJson } from "./blockPackageJson.js"; -import { getPackageDependencies, getPackageDependency } from "./packageData.js"; import { CommandPhase } from "./phases.js"; export const blockKnip = base.createBlock({ diff --git a/src/next/blocks/blockMITLicense.ts b/src/blocks/blockMITLicense.ts similarity index 100% rename from src/next/blocks/blockMITLicense.ts rename to src/blocks/blockMITLicense.ts diff --git a/src/next/blocks/blockMarkdownlint.test.ts b/src/blocks/blockMarkdownlint.test.ts similarity index 100% rename from src/next/blocks/blockMarkdownlint.test.ts rename to src/blocks/blockMarkdownlint.test.ts diff --git a/src/next/blocks/blockMarkdownlint.ts b/src/blocks/blockMarkdownlint.ts similarity index 96% rename from src/next/blocks/blockMarkdownlint.ts rename to src/blocks/blockMarkdownlint.ts index 0f1cd9002..4410f601c 100644 --- a/src/next/blocks/blockMarkdownlint.ts +++ b/src/blocks/blockMarkdownlint.ts @@ -1,12 +1,12 @@ import { z } from "zod"; import { base } from "../base.js"; +import { getPackageDependencies } from "../data/packageData.js"; import { blockCSpell } from "./blockCSpell.js"; import { blockDevelopmentDocs } from "./blockDevelopmentDocs.js"; import { blockGitHubActionsCI } from "./blockGitHubActionsCI.js"; import { blockPackageJson } from "./blockPackageJson.js"; import { blockVSCode } from "./blockVSCode.js"; -import { getPackageDependencies } from "./packageData.js"; import { CommandPhase } from "./phases.js"; export const blockMarkdownlint = base.createBlock({ diff --git a/src/next/blocks/blockNvmrc.test.ts b/src/blocks/blockNvmrc.test.ts similarity index 100% rename from src/next/blocks/blockNvmrc.test.ts rename to src/blocks/blockNvmrc.test.ts diff --git a/src/next/blocks/blockNvmrc.ts b/src/blocks/blockNvmrc.ts similarity index 100% rename from src/next/blocks/blockNvmrc.ts rename to src/blocks/blockNvmrc.ts diff --git a/src/next/blocks/blockPRCompliance.ts b/src/blocks/blockPRCompliance.ts similarity index 89% rename from src/next/blocks/blockPRCompliance.ts rename to src/blocks/blockPRCompliance.ts index eac9a64cd..a671555ca 100644 --- a/src/next/blocks/blockPRCompliance.ts +++ b/src/blocks/blockPRCompliance.ts @@ -1,5 +1,5 @@ -import { createSoloWorkflowFile } from "../../steps/writing/creation/dotGitHub/createSoloWorkflowFile.js"; import { base } from "../base.js"; +import { createSoloWorkflowFile } from "./files/createSoloWorkflowFile.js"; export const blockPRCompliance = base.createBlock({ about: { diff --git a/src/next/blocks/blockPackageJson.test.ts b/src/blocks/blockPackageJson.test.ts similarity index 100% rename from src/next/blocks/blockPackageJson.test.ts rename to src/blocks/blockPackageJson.test.ts diff --git a/src/next/blocks/blockPackageJson.ts b/src/blocks/blockPackageJson.ts similarity index 100% rename from src/next/blocks/blockPackageJson.ts rename to src/blocks/blockPackageJson.ts diff --git a/src/next/blocks/blockPnpmDedupe.ts b/src/blocks/blockPnpmDedupe.ts similarity index 100% rename from src/next/blocks/blockPnpmDedupe.ts rename to src/blocks/blockPnpmDedupe.ts diff --git a/src/next/blocks/blockPrettier.test.ts b/src/blocks/blockPrettier.test.ts similarity index 98% rename from src/next/blocks/blockPrettier.test.ts rename to src/blocks/blockPrettier.test.ts index e54d2c6c7..ef447c7d8 100644 --- a/src/next/blocks/blockPrettier.test.ts +++ b/src/blocks/blockPrettier.test.ts @@ -60,7 +60,7 @@ describe("blockPrettier", () => { "properties": { "devDependencies": { "husky": "9.1.7", - "lint-staged": "15.2.11", + "lint-staged": "15.3.0", "prettier": "^3.4.2", }, "lint-staged": { @@ -171,7 +171,7 @@ describe("blockPrettier", () => { "properties": { "devDependencies": { "husky": "9.1.7", - "lint-staged": "15.2.11", + "lint-staged": "15.3.0", "prettier": "^3.4.2", }, "lint-staged": { @@ -296,7 +296,7 @@ describe("blockPrettier", () => { "properties": { "devDependencies": { "husky": "9.1.7", - "lint-staged": "15.2.11", + "lint-staged": "15.3.0", "prettier": "^3.4.2", "prettier-plugin-curly": "0.3.1", "prettier-plugin-packagejson": "2.5.6", diff --git a/src/next/blocks/blockPrettier.ts b/src/blocks/blockPrettier.ts similarity index 97% rename from src/next/blocks/blockPrettier.ts rename to src/blocks/blockPrettier.ts index bce284e4c..f08f1c4fb 100644 --- a/src/next/blocks/blockPrettier.ts +++ b/src/blocks/blockPrettier.ts @@ -1,12 +1,12 @@ import { z } from "zod"; import { base } from "../base.js"; +import { getPackageDependencies } from "../data/packageData.js"; import { blockCSpell } from "./blockCSpell.js"; import { blockDevelopmentDocs } from "./blockDevelopmentDocs.js"; import { blockGitHubActionsCI } from "./blockGitHubActionsCI.js"; import { blockPackageJson } from "./blockPackageJson.js"; import { blockVSCode } from "./blockVSCode.js"; -import { getPackageDependencies } from "./packageData.js"; import { CommandPhase } from "./phases.js"; export const blockPrettier = base.createBlock({ diff --git a/src/next/blocks/blockPrettierPluginCurly.ts b/src/blocks/blockPrettierPluginCurly.ts similarity index 100% rename from src/next/blocks/blockPrettierPluginCurly.ts rename to src/blocks/blockPrettierPluginCurly.ts diff --git a/src/next/blocks/blockPrettierPluginPackageJson.ts b/src/blocks/blockPrettierPluginPackageJson.ts similarity index 100% rename from src/next/blocks/blockPrettierPluginPackageJson.ts rename to src/blocks/blockPrettierPluginPackageJson.ts diff --git a/src/next/blocks/blockPrettierPluginSh.ts b/src/blocks/blockPrettierPluginSh.ts similarity index 100% rename from src/next/blocks/blockPrettierPluginSh.ts rename to src/blocks/blockPrettierPluginSh.ts diff --git a/src/next/blocks/blockREADME.test.ts b/src/blocks/blockREADME.test.ts similarity index 100% rename from src/next/blocks/blockREADME.test.ts rename to src/blocks/blockREADME.test.ts diff --git a/src/next/blocks/blockREADME.ts b/src/blocks/blockREADME.ts similarity index 100% rename from src/next/blocks/blockREADME.ts rename to src/blocks/blockREADME.ts diff --git a/src/next/blocks/blockReleaseIt.ts b/src/blocks/blockReleaseIt.ts similarity index 95% rename from src/next/blocks/blockReleaseIt.ts rename to src/blocks/blockReleaseIt.ts index 29d4e2d01..fb909f95c 100644 --- a/src/next/blocks/blockReleaseIt.ts +++ b/src/blocks/blockReleaseIt.ts @@ -1,9 +1,9 @@ -import { createSoloWorkflowFile } from "../../steps/writing/creation/dotGitHub/createSoloWorkflowFile.js"; import { base } from "../base.js"; +import { getPackageDependencies } from "../data/packageData.js"; import { blockCSpell } from "./blockCSpell.js"; import { blockPackageJson } from "./blockPackageJson.js"; import { blockRepositorySecrets } from "./blockRepositorySecrets.js"; -import { getPackageDependencies } from "./packageData.js"; +import { createSoloWorkflowFile } from "./files/createSoloWorkflowFile.js"; export const blockReleaseIt = base.createBlock({ about: { diff --git a/src/next/blocks/blockRenovate.ts b/src/blocks/blockRenovate.ts similarity index 100% rename from src/next/blocks/blockRenovate.ts rename to src/blocks/blockRenovate.ts diff --git a/src/next/blocks/blockRepositoryBranchRuleset.test.ts b/src/blocks/blockRepositoryBranchRuleset.test.ts similarity index 100% rename from src/next/blocks/blockRepositoryBranchRuleset.test.ts rename to src/blocks/blockRepositoryBranchRuleset.test.ts diff --git a/src/next/blocks/blockRepositoryBranchRuleset.ts b/src/blocks/blockRepositoryBranchRuleset.ts similarity index 100% rename from src/next/blocks/blockRepositoryBranchRuleset.ts rename to src/blocks/blockRepositoryBranchRuleset.ts diff --git a/src/next/blocks/blockRepositoryLabels.ts b/src/blocks/blockRepositoryLabels.ts similarity index 84% rename from src/next/blocks/blockRepositoryLabels.ts rename to src/blocks/blockRepositoryLabels.ts index b8980788b..4b00f32dc 100644 --- a/src/next/blocks/blockRepositoryLabels.ts +++ b/src/blocks/blockRepositoryLabels.ts @@ -1,7 +1,7 @@ import { setGitHubRepositoryLabels } from "set-github-repository-labels"; -import { outcomeLabels } from "../../steps/initializeGitHubRepository/outcomeLabels.js"; import { base } from "../base.js"; +import { outcomeLabels } from "./outcomeLabels.js"; export const blockRepositoryLabels = base.createBlock({ about: { diff --git a/src/next/blocks/blockRepositorySecrets.ts b/src/blocks/blockRepositorySecrets.ts similarity index 100% rename from src/next/blocks/blockRepositorySecrets.ts rename to src/blocks/blockRepositorySecrets.ts diff --git a/src/next/blocks/blockRepositorySettings.ts b/src/blocks/blockRepositorySettings.ts similarity index 100% rename from src/next/blocks/blockRepositorySettings.ts rename to src/blocks/blockRepositorySettings.ts diff --git a/src/next/blocks/blockSecurityDocs.ts b/src/blocks/blockSecurityDocs.ts similarity index 100% rename from src/next/blocks/blockSecurityDocs.ts rename to src/blocks/blockSecurityDocs.ts diff --git a/src/next/blocks/blockTSup.test.ts b/src/blocks/blockTSup.test.ts similarity index 100% rename from src/next/blocks/blockTSup.test.ts rename to src/blocks/blockTSup.test.ts diff --git a/src/next/blocks/blockTSup.ts b/src/blocks/blockTSup.ts similarity index 96% rename from src/next/blocks/blockTSup.ts rename to src/blocks/blockTSup.ts index dd7f01fcb..56efc77a2 100644 --- a/src/next/blocks/blockTSup.ts +++ b/src/blocks/blockTSup.ts @@ -1,11 +1,11 @@ import { z } from "zod"; import { base } from "../base.js"; +import { getPackageDependencies } from "../data/packageData.js"; import { blockDevelopmentDocs } from "./blockDevelopmentDocs.js"; import { blockESLint } from "./blockESLint.js"; import { blockGitHubActionsCI } from "./blockGitHubActionsCI.js"; import { blockPackageJson } from "./blockPackageJson.js"; -import { getPackageDependencies } from "./packageData.js"; import { CommandPhase } from "./phases.js"; export const blockTSup = base.createBlock({ diff --git a/src/next/blocks/blockTemplatedBy.ts b/src/blocks/blockTemplatedWith.ts similarity index 91% rename from src/next/blocks/blockTemplatedBy.ts rename to src/blocks/blockTemplatedWith.ts index ab85c72cb..9d9d2c38f 100644 --- a/src/next/blocks/blockTemplatedBy.ts +++ b/src/blocks/blockTemplatedWith.ts @@ -2,7 +2,7 @@ import { base } from "../base.js"; import { blockCSpell } from "./blockCSpell.js"; import { blockREADME } from "./blockREADME.js"; -export const blockTemplatedBy = base.createBlock({ +export const blockTemplatedWith = base.createBlock({ about: { name: "Templated By Notice", }, diff --git a/src/next/blocks/blockTypeScript.test.ts b/src/blocks/blockTypeScript.test.ts similarity index 100% rename from src/next/blocks/blockTypeScript.test.ts rename to src/blocks/blockTypeScript.test.ts diff --git a/src/next/blocks/blockTypeScript.ts b/src/blocks/blockTypeScript.ts similarity index 98% rename from src/next/blocks/blockTypeScript.ts rename to src/blocks/blockTypeScript.ts index 9da798617..ccd575f1a 100644 --- a/src/next/blocks/blockTypeScript.ts +++ b/src/blocks/blockTypeScript.ts @@ -1,4 +1,5 @@ import { base } from "../base.js"; +import { getPackageDependencies } from "../data/packageData.js"; import { blockDevelopmentDocs } from "./blockDevelopmentDocs.js"; import { blockExampleFiles } from "./blockExampleFiles.js"; import { blockGitHubActionsCI } from "./blockGitHubActionsCI.js"; @@ -7,7 +8,6 @@ import { blockMarkdownlint } from "./blockMarkdownlint.js"; import { blockPackageJson } from "./blockPackageJson.js"; import { blockVitest } from "./blockVitest.js"; import { blockVSCode } from "./blockVSCode.js"; -import { getPackageDependencies } from "./packageData.js"; export const blockTypeScript = base.createBlock({ about: { diff --git a/src/next/blocks/blockVSCode.test.ts b/src/blocks/blockVSCode.test.ts similarity index 100% rename from src/next/blocks/blockVSCode.test.ts rename to src/blocks/blockVSCode.test.ts diff --git a/src/next/blocks/blockVSCode.ts b/src/blocks/blockVSCode.ts similarity index 100% rename from src/next/blocks/blockVSCode.ts rename to src/blocks/blockVSCode.ts diff --git a/src/next/blocks/blockVitest.test.ts b/src/blocks/blockVitest.test.ts similarity index 100% rename from src/next/blocks/blockVitest.test.ts rename to src/blocks/blockVitest.test.ts diff --git a/src/next/blocks/blockVitest.ts b/src/blocks/blockVitest.ts similarity index 98% rename from src/next/blocks/blockVitest.ts rename to src/blocks/blockVitest.ts index d1281b79d..ee03c306c 100644 --- a/src/next/blocks/blockVitest.ts +++ b/src/blocks/blockVitest.ts @@ -1,6 +1,7 @@ import { z } from "zod"; import { base } from "../base.js"; +import { getPackageDependencies } from "../data/packageData.js"; import { blockCSpell } from "./blockCSpell.js"; import { blockDevelopmentDocs } from "./blockDevelopmentDocs.js"; import { blockESLint } from "./blockESLint.js"; @@ -12,7 +13,6 @@ import { blockPackageJson } from "./blockPackageJson.js"; import { blockPrettier } from "./blockPrettier.js"; import { blockTSup } from "./blockTSup.js"; import { blockVSCode } from "./blockVSCode.js"; -import { getPackageDependencies } from "./packageData.js"; import { CommandPhase } from "./phases.js"; export const blockVitest = base.createBlock({ diff --git a/src/steps/writing/creation/dotGitHub/createMultiWorkflowFile.ts b/src/blocks/files/createMultiWorkflowFile.ts similarity index 100% rename from src/steps/writing/creation/dotGitHub/createMultiWorkflowFile.ts rename to src/blocks/files/createMultiWorkflowFile.ts diff --git a/src/steps/writing/creation/dotGitHub/createSoloWorkflowFile.ts b/src/blocks/files/createSoloWorkflowFile.ts similarity index 100% rename from src/steps/writing/creation/dotGitHub/createSoloWorkflowFile.ts rename to src/blocks/files/createSoloWorkflowFile.ts diff --git a/src/steps/writing/creation/formatters/formatIgnoreFile.ts b/src/blocks/files/formatIgnoreFile.ts similarity index 100% rename from src/steps/writing/creation/formatters/formatIgnoreFile.ts rename to src/blocks/files/formatIgnoreFile.ts diff --git a/src/steps/writing/creation/dotGitHub/formatWorkflowYaml.ts b/src/blocks/files/formatWorkflowYaml.ts similarity index 82% rename from src/steps/writing/creation/dotGitHub/formatWorkflowYaml.ts rename to src/blocks/files/formatWorkflowYaml.ts index b2dbfefe6..c7e088372 100644 --- a/src/steps/writing/creation/dotGitHub/formatWorkflowYaml.ts +++ b/src/blocks/files/formatWorkflowYaml.ts @@ -1,4 +1,4 @@ -import { formatYaml } from "../formatters/formatYaml.js"; +import { formatYaml } from "./formatYaml.js"; export function formatWorkflowYaml(value: unknown) { return ( diff --git a/src/steps/writing/creation/formatters/formatYaml.ts b/src/blocks/files/formatYaml.ts similarity index 78% rename from src/steps/writing/creation/formatters/formatYaml.ts rename to src/blocks/files/formatYaml.ts index ef7245d44..8ad1ff528 100644 --- a/src/steps/writing/creation/formatters/formatYaml.ts +++ b/src/blocks/files/formatYaml.ts @@ -1,4 +1,3 @@ -import prettier from "@prettier/sync"; import jsYaml from "js-yaml"; const options: jsYaml.DumpOptions = { @@ -22,6 +21,5 @@ const options: jsYaml.DumpOptions = { }; export function formatYaml(value: unknown) { - const dumped = jsYaml.dump(value, options); - return prettier.format(dumped, { parser: "yaml" }); + return jsYaml.dump(value, options); } diff --git a/src/blocks/index.ts b/src/blocks/index.ts new file mode 100644 index 000000000..efc0fb385 --- /dev/null +++ b/src/blocks/index.ts @@ -0,0 +1,130 @@ +import { blockAllContributors } from "./blockAllContributors.js"; +import { blockAreTheTypesWrong } from "./blockAreTheTypesWrong.js"; +import { blockContributingDocs } from "./blockContributingDocs.js"; +import { blockContributorCovenant } from "./blockContributorCovenant.js"; +import { blockCSpell } from "./blockCSpell.js"; +import { blockDevelopmentDocs } from "./blockDevelopmentDocs.js"; +import { blockESLint } from "./blockESLint.js"; +import { blockESLintComments } from "./blockESLintComments.js"; +import { blockESLintJSDoc } from "./blockESLintJSDoc.js"; +import { blockESLintJSONC } from "./blockESLintJSONC.js"; +import { blockESLintMarkdown } from "./blockESLintMarkdown.js"; +import { blockESLintMoreStyling } from "./blockESLintMoreStyling.js"; +import { blockESLintNode } from "./blockESLintNode.js"; +import { blockESLintPackageJson } from "./blockESLintPackageJson.js"; +import { blockESLintPerfectionist } from "./blockESLintPerfectionist.js"; +import { blockESLintRegexp } from "./blockESLintRegexp.js"; +import { blockESLintYML } from "./blockESLintYML.js"; +import { blockFunding } from "./blockFunding.js"; +import { blockGitHubActionsCI } from "./blockGitHubActionsCI.js"; +import { blockGitHubIssueTemplates } from "./blockGitHubIssueTemplates.js"; +import { blockGitHubPRTemplate } from "./blockGitHubPRTemplate.js"; +import { blockGitignore } from "./blockGitignore.js"; +import { blockKnip } from "./blockKnip.js"; +import { blockMarkdownlint } from "./blockMarkdownlint.js"; +import { blockMITLicense } from "./blockMITLicense.js"; +import { blockNvmrc } from "./blockNvmrc.js"; +import { blockPackageJson } from "./blockPackageJson.js"; +import { blockPnpmDedupe } from "./blockPnpmDedupe.js"; +import { blockPRCompliance } from "./blockPRCompliance.js"; +import { blockPrettier } from "./blockPrettier.js"; +import { blockPrettierPluginCurly } from "./blockPrettierPluginCurly.js"; +import { blockPrettierPluginPackageJson } from "./blockPrettierPluginPackageJson.js"; +import { blockPrettierPluginSh } from "./blockPrettierPluginSh.js"; +import { blockREADME } from "./blockREADME.js"; +import { blockReleaseIt } from "./blockReleaseIt.js"; +import { blockRenovate } from "./blockRenovate.js"; +import { blockSecurityDocs } from "./blockSecurityDocs.js"; +import { blockTemplatedWith } from "./blockTemplatedWith.js"; +import { blockTSup } from "./blockTSup.js"; +import { blockTypeScript } from "./blockTypeScript.js"; +import { blockVitest } from "./blockVitest.js"; +import { blockVSCode } from "./blockVSCode.js"; + +export const blocks = { + blockAllContributors, + blockAreTheTypesWrong, + blockContributingDocs, + blockContributorCovenant, + blockCSpell, + blockDevelopmentDocs, + blockESLint, + blockESLintComments, + blockESLintJSDoc, + blockESLintJSONC, + blockESLintMarkdown, + blockESLintMoreStyling, + blockESLintNode, + blockESLintPackageJson, + blockESLintPerfectionist, + blockESLintRegexp, + blockESLintYML, + blockFunding, + blockGitHubActionsCI, + blockGitHubIssueTemplates, + blockGitHubPRTemplate, + blockGitignore, + blockKnip, + blockMarkdownlint, + blockMITLicense, + blockNvmrc, + blockPackageJson, + blockPnpmDedupe, + blockPRCompliance, + blockPrettier, + blockPrettierPluginCurly, + blockPrettierPluginPackageJson, + blockPrettierPluginSh, + blockREADME, + blockReleaseIt, + blockRenovate, + blockSecurityDocs, + blockTemplatedWith, + blockTSup, + blockTypeScript, + blockVitest, + blockVSCode, +}; + +export { blockAllContributors } from "./blockAllContributors.js"; +export { blockAreTheTypesWrong } from "./blockAreTheTypesWrong.js"; +export { blockContributingDocs } from "./blockContributingDocs.js"; +export { blockContributorCovenant } from "./blockContributorCovenant.js"; +export { blockCSpell } from "./blockCSpell.js"; +export { blockDevelopmentDocs } from "./blockDevelopmentDocs.js"; +export { blockESLint } from "./blockESLint.js"; +export { blockESLintComments } from "./blockESLintComments.js"; +export { blockESLintJSDoc } from "./blockESLintJSDoc.js"; +export { blockESLintJSONC } from "./blockESLintJSONC.js"; +export { blockESLintMarkdown } from "./blockESLintMarkdown.js"; +export { blockESLintMoreStyling } from "./blockESLintMoreStyling.js"; +export { blockESLintNode } from "./blockESLintNode.js"; +export { blockESLintPackageJson } from "./blockESLintPackageJson.js"; +export { blockESLintPerfectionist } from "./blockESLintPerfectionist.js"; +export { blockESLintRegexp } from "./blockESLintRegexp.js"; +export { blockESLintYML } from "./blockESLintYML.js"; +export { blockFunding } from "./blockFunding.js"; +export { blockGitHubActionsCI } from "./blockGitHubActionsCI.js"; +export { blockGitHubIssueTemplates } from "./blockGitHubIssueTemplates.js"; +export { blockGitHubPRTemplate } from "./blockGitHubPRTemplate.js"; +export { blockGitignore } from "./blockGitignore.js"; +export { blockKnip } from "./blockKnip.js"; +export { blockMarkdownlint } from "./blockMarkdownlint.js"; +export { blockMITLicense } from "./blockMITLicense.js"; +export { blockNvmrc } from "./blockNvmrc.js"; +export { blockPackageJson } from "./blockPackageJson.js"; +export { blockPnpmDedupe } from "./blockPnpmDedupe.js"; +export { blockPRCompliance } from "./blockPRCompliance.js"; +export { blockPrettier } from "./blockPrettier.js"; +export { blockPrettierPluginCurly } from "./blockPrettierPluginCurly.js"; +export { blockPrettierPluginPackageJson } from "./blockPrettierPluginPackageJson.js"; +export { blockPrettierPluginSh } from "./blockPrettierPluginSh.js"; +export { blockREADME } from "./blockREADME.js"; +export { blockReleaseIt } from "./blockReleaseIt.js"; +export { blockRenovate } from "./blockRenovate.js"; +export { blockSecurityDocs } from "./blockSecurityDocs.js"; +export { blockTemplatedWith } from "./blockTemplatedWith.js"; +export { blockTSup } from "./blockTSup.js"; +export { blockTypeScript } from "./blockTypeScript.js"; +export { blockVitest } from "./blockVitest.js"; +export { blockVSCode } from "./blockVSCode.js"; diff --git a/src/next/blocks/options.fakes.ts b/src/blocks/options.fakes.ts similarity index 100% rename from src/next/blocks/options.fakes.ts rename to src/blocks/options.fakes.ts diff --git a/src/steps/initializeGitHubRepository/outcomeLabels.ts b/src/blocks/outcomeLabels.ts similarity index 98% rename from src/steps/initializeGitHubRepository/outcomeLabels.ts rename to src/blocks/outcomeLabels.ts index 092bfd5bc..e589e97eb 100644 --- a/src/steps/initializeGitHubRepository/outcomeLabels.ts +++ b/src/blocks/outcomeLabels.ts @@ -1,4 +1,3 @@ -/* spellchecker: disable */ export const outcomeLabels = [ { aliases: ["docs"], @@ -75,6 +74,7 @@ export const outcomeLabels = [ }, { aliases: ["enhancement"], + /* spellchecker: disable-next-line */ color: "a2eeef", description: "New enhancement or request 🚀", name: "type: feature", diff --git a/src/next/blocks/phases.ts b/src/blocks/phases.ts similarity index 100% rename from src/next/blocks/phases.ts rename to src/blocks/phases.ts diff --git a/src/create/createAndEnterGitDirectory.test.ts b/src/create/createAndEnterGitDirectory.test.ts deleted file mode 100644 index 85ceb3a02..000000000 --- a/src/create/createAndEnterGitDirectory.test.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; - -import { createAndEnterGitDirectory } from "./createAndEnterGitDirectory.js"; - -const mockMkdir = vi.fn(); -const mockReaddir = vi.fn(); - -vi.mock("node:fs/promises", () => ({ - get mkdir() { - return mockMkdir; - }, - get readdir() { - return mockReaddir; - }, -})); - -const mockChdir = vi.fn(); -const mockCwd = "/path/to/cwd"; - -vi.mock("node:process", () => ({ - get chdir() { - return mockChdir; - }, - cwd() { - return mockCwd; - }, -})); - -const mock$$ = vi.fn(); - -const mock$ = vi.fn().mockReturnValue(mock$$); - -vi.mock("execa", () => ({ - get $() { - return mock$; - }, -})); - -describe("createAndEnterGitDirectory", () => { - it("returns false and doesn't run fs.mkdir when the directory is '.' and has children", async () => { - mockReaddir.mockResolvedValueOnce(["file"]); - - const actual = await createAndEnterGitDirectory("."); - - expect(actual).toBeUndefined(); - expect(mockMkdir).not.toHaveBeenCalled(); - }); - - it("returns true and doesn't run fs.mkdir when the directory is '.' and is empty", async () => { - mockReaddir.mockResolvedValueOnce([]); - - const actual = await createAndEnterGitDirectory("."); - - expect(actual).toBe(mockCwd); - expect(mockMkdir).not.toHaveBeenCalled(); - }); - - it("returns false and doesn't run process.chdir when the directory is a child directory with children", async () => { - const directory = "dir"; - mockReaddir - .mockResolvedValueOnce([directory]) - .mockResolvedValueOnce(["file"]); - - const actual = await createAndEnterGitDirectory(directory); - - expect(actual).toBeUndefined(); - expect(mockChdir).not.toHaveBeenCalled(); - expect(mock$).not.toHaveBeenCalled(); - }); - - it("returns true and runs process.chdir when the directory is a child directory that doesn't exist", async () => { - const directory = "dir"; - mockReaddir.mockResolvedValueOnce([directory]).mockResolvedValueOnce([]); - - const actual = await createAndEnterGitDirectory(directory); - - expect(actual).toBe(mockCwd); - expect(mockChdir).toHaveBeenCalledWith(directory); - expect(mock$).toHaveBeenCalledWith({ cwd: mockCwd }); - expect(mock$$).toHaveBeenCalledWith(["git init -b main"]); - }); -}); diff --git a/src/create/createAndEnterGitDirectory.ts b/src/create/createAndEnterGitDirectory.ts deleted file mode 100644 index c249d823d..000000000 --- a/src/create/createAndEnterGitDirectory.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { $ } from "execa"; -import * as fs from "node:fs/promises"; -import * as process from "node:process"; - -export async function createAndEnterGitDirectory(directory: string) { - if (directory !== "." && !(await fs.readdir(".")).includes(directory)) { - await fs.mkdir(directory); - } else if ((await fs.readdir(directory)).length) { - return undefined; - } - - if (directory !== ".") { - process.chdir(directory); - } - - // https://github.com/JoshuaKGoldberg/create-typescript-app/pull/1781 - // For some reason, after this function returns, the cwd was resetting back? - // Also $ commands seem to preserve the cwd from the start of the script?! - // Maybe some parallelized running of scripts was messing with it? - // All this code will be gone soon once the create engine takes over anyway. - const cwd = process.cwd(); - - await $({ cwd })`git init -b main`; - - return cwd; -} diff --git a/src/create/createRerunDirectorySuggestion.test.ts b/src/create/createRerunDirectorySuggestion.test.ts deleted file mode 100644 index 9b4798348..000000000 --- a/src/create/createRerunDirectorySuggestion.test.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { describe, expect, it } from "vitest"; - -import { createRerunDirectorySuggestion } from "./createRerunDirectorySuggestion.js"; - -const directory = "test-directory"; -const repository = "test-repository"; - -describe("createRerunDirectorySuggestion", () => { - it("returns undefined when mode is create and directory matches repository", () => { - const suggestion = createRerunDirectorySuggestion({ - directory: repository, - mode: "create", - repository, - }); - - expect(suggestion).toBe(undefined); - }); - - it("returns directory when mode is create and directory doesn't match repository", () => { - const suggestion = createRerunDirectorySuggestion({ - directory, - mode: "create", - repository, - }); - - expect(suggestion).toBe(directory); - }); - - it("returns undefined when mode is initialize and directory is .", () => { - const suggestion = createRerunDirectorySuggestion({ - directory: ".", - mode: "initialize", - repository, - }); - - expect(suggestion).toBe(undefined); - }); - - it("returns directory when mode is initialize and directory is not .", () => { - const suggestion = createRerunDirectorySuggestion({ - directory, - mode: "initialize", - repository, - }); - - expect(suggestion).toBe(directory); - }); -}); diff --git a/src/create/createRerunDirectorySuggestion.ts b/src/create/createRerunDirectorySuggestion.ts deleted file mode 100644 index 307299423..000000000 --- a/src/create/createRerunDirectorySuggestion.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Options } from "../shared/types.js"; - -export function createRerunDirectorySuggestion(options: Partial) { - const defaultValue = options.mode === "create" ? options.repository : "."; - - return options.directory === defaultValue ? undefined : options.directory; -} diff --git a/src/create/createRerunSuggestion.test.ts b/src/create/createRerunSuggestion.test.ts deleted file mode 100644 index 856d55c9c..000000000 --- a/src/create/createRerunSuggestion.test.ts +++ /dev/null @@ -1,185 +0,0 @@ -import { describe, expect, it } from "vitest"; - -import { getExclusions } from "../shared/options/exclusionKeys.js"; -import { Options } from "../shared/types.js"; -import { createRerunSuggestion } from "./createRerunSuggestion.js"; - -const options = { - access: "public", - author: "TestAuthor", - base: "everything", - description: "Test description.", - directory: ".", - email: { - github: "github@email.com", - npm: "npm@email.com", - }, - excludeAllContributors: true, - excludeCompliance: true, - excludeLintJSDoc: true, - excludeLintJson: true, - excludeLintKnip: true, - excludeLintMd: false, - excludeLintPackageJson: true, - excludeLintPackages: false, - excludeLintPerfectionist: true, - excludeLintSpelling: false, - excludeLintYml: false, - excludeReleases: false, - excludeRenovate: undefined, - excludeTemplatedBy: undefined, - excludeTests: undefined, - funding: undefined, - keywords: ["abc", "def ghi", "jkl mno pqr"], - mode: "create", - owner: "TestOwner", - repository: "test-repository", - skipGitHubApi: true, - skipInstall: true, - skipRemoval: true, - skipRestore: undefined, - skipUninstall: undefined, - title: "Test Title", -} satisfies Options; - -describe("createRerunSuggestion", () => { - it("prints no options when no options are provided", () => { - const actual = createRerunSuggestion({}); - - expect(actual).toMatchInlineSnapshot(`"npx create-typescript-app"`); - }); - - it("prints only mode when no other options are provided", () => { - const actual = createRerunSuggestion({ - mode: "create", - }); - - expect(actual).toMatchInlineSnapshot( - `"npx create-typescript-app --mode create"`, - ); - }); - - it("includes key-value pairs with mixed truthy and falsy values", () => { - const actual = createRerunSuggestion(options); - - expect(actual).toMatchInlineSnapshot( - `"npx create-typescript-app --base everything --author TestAuthor --description "Test description." --directory . --email-github github@email.com --email-npm npm@email.com --exclude-all-contributors --exclude-compliance --exclude-lint-jsdoc --exclude-lint-json --exclude-lint-knip --exclude-lint-package-json --exclude-lint-perfectionist --keywords "abc def ghi jkl mno pqr" --mode create --owner TestOwner --repository test-repository --skip-github-api --skip-install --skip-removal --title "Test Title""`, - ); - }); - - it("includes a non-default value when specified", () => { - const actual = createRerunSuggestion({ - ...options, - access: "restricted", - }); - - expect(actual).toMatchInlineSnapshot( - `"npx create-typescript-app --base everything --access restricted --author TestAuthor --description "Test description." --directory . --email-github github@email.com --email-npm npm@email.com --exclude-all-contributors --exclude-compliance --exclude-lint-jsdoc --exclude-lint-json --exclude-lint-knip --exclude-lint-package-json --exclude-lint-perfectionist --keywords "abc def ghi jkl mno pqr" --mode create --owner TestOwner --repository test-repository --skip-github-api --skip-install --skip-removal --title "Test Title""`, - ); - }); - - it("includes stringified guide when it exists", () => { - const actual = createRerunSuggestion({ - ...options, - guide: { - href: "https://example.com", - title: "Test Title", - }, - mode: "initialize", - }); - - expect(actual).toMatchInlineSnapshot( - `"npx create-typescript-app --base everything --author TestAuthor --description "Test description." --email-github github@email.com --email-npm npm@email.com --exclude-all-contributors --exclude-compliance --exclude-lint-jsdoc --exclude-lint-json --exclude-lint-knip --exclude-lint-package-json --exclude-lint-perfectionist --guide https://example.com --guide-title "Test Title" --keywords "abc def ghi jkl mno pqr" --mode initialize --owner TestOwner --repository test-repository --skip-github-api --skip-install --skip-removal --title "Test Title""`, - ); - }); - - it("includes stringified logo when it exists", () => { - const actual = createRerunSuggestion({ - ...options, - logo: { - alt: "Test alt.", - src: "test/src.png", - }, - mode: "initialize", - }); - - expect(actual).toMatchInlineSnapshot( - `"npx create-typescript-app --base everything --author TestAuthor --description "Test description." --email-github github@email.com --email-npm npm@email.com --exclude-all-contributors --exclude-compliance --exclude-lint-jsdoc --exclude-lint-json --exclude-lint-knip --exclude-lint-package-json --exclude-lint-perfectionist --keywords "abc def ghi jkl mno pqr" --logo test/src.png --logo-alt "Test alt." --mode initialize --owner TestOwner --repository test-repository --skip-github-api --skip-install --skip-removal --title "Test Title""`, - ); - }); - - it("does not include directory when it is repository and the mode is create", () => { - const actual = createRerunSuggestion({ - ...options, - directory: options.repository, - mode: "create", - }); - - expect(actual).toMatchInlineSnapshot( - `"npx create-typescript-app --base everything --author TestAuthor --description "Test description." --email-github github@email.com --email-npm npm@email.com --exclude-all-contributors --exclude-compliance --exclude-lint-jsdoc --exclude-lint-json --exclude-lint-knip --exclude-lint-package-json --exclude-lint-perfectionist --keywords "abc def ghi jkl mno pqr" --mode create --owner TestOwner --repository test-repository --skip-github-api --skip-install --skip-removal --title "Test Title""`, - ); - }); - - it("includes directory when it is repository and the mode is migrate", () => { - const actual = createRerunSuggestion({ - ...options, - directory: options.repository, - mode: "migrate", - }); - - expect(actual).toMatchInlineSnapshot( - `"npx create-typescript-app --base everything --author TestAuthor --description "Test description." --directory test-repository --email-github github@email.com --email-npm npm@email.com --exclude-all-contributors --exclude-compliance --exclude-lint-jsdoc --exclude-lint-json --exclude-lint-knip --exclude-lint-package-json --exclude-lint-perfectionist --keywords "abc def ghi jkl mno pqr" --mode migrate --owner TestOwner --repository test-repository --skip-github-api --skip-install --skip-removal --title "Test Title""`, - ); - }); - - it("includes exclusions when they exist", () => { - const actual = createRerunSuggestion({ - ...options, - excludeCompliance: true, - excludeLintMd: true, - excludeLintSpelling: true, - mode: "initialize", - }); - - expect(actual).toMatchInlineSnapshot( - `"npx create-typescript-app --base everything --author TestAuthor --description "Test description." --email-github github@email.com --email-npm npm@email.com --exclude-all-contributors --exclude-compliance --exclude-lint-jsdoc --exclude-lint-json --exclude-lint-knip --exclude-lint-md --exclude-lint-package-json --exclude-lint-perfectionist --exclude-lint-spelling --keywords "abc def ghi jkl mno pqr" --mode initialize --owner TestOwner --repository test-repository --skip-github-api --skip-install --skip-removal --title "Test Title""`, - ); - }); - - it("does not list all excludes when using common base", () => { - const common = createRerunSuggestion({ - base: "common", - ...getExclusions(options, "common"), - excludeLintKnip: undefined, - }); - - expect(common).toMatchInlineSnapshot( - `"npx create-typescript-app --base common"`, - ); - }); - - it("does not list all excludes when using minimal base", () => { - const minimal = createRerunSuggestion({ - base: "minimal", - ...getExclusions(options, "minimal"), - excludeLintKnip: undefined, - }); - - expect(minimal).toMatchInlineSnapshot( - `"npx create-typescript-app --base minimal"`, - ); - }); - - it("does not list API skip flags when --offline is true", () => { - const actual = createRerunSuggestion({ - ...options, - offline: true, - skipAllContributorsApi: true, - skipGitHubApi: true, - }); - - expect(actual).toMatchInlineSnapshot( - `"npx create-typescript-app --base everything --author TestAuthor --description "Test description." --directory . --email-github github@email.com --email-npm npm@email.com --exclude-all-contributors --exclude-compliance --exclude-lint-jsdoc --exclude-lint-json --exclude-lint-knip --exclude-lint-package-json --exclude-lint-perfectionist --keywords "abc def ghi jkl mno pqr" --mode create --offline --owner TestOwner --repository test-repository --skip-install --skip-removal --title "Test Title""`, - ); - }); -}); diff --git a/src/create/createRerunSuggestion.ts b/src/create/createRerunSuggestion.ts deleted file mode 100644 index 0ffb97865..000000000 --- a/src/create/createRerunSuggestion.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { allArgOptions } from "../shared/options/args.js"; -import { - ExclusionKey, - getExclusions, -} from "../shared/options/exclusionKeys.js"; -import { Options } from "../shared/types.js"; -import { createRerunDirectorySuggestion } from "./createRerunDirectorySuggestion.js"; - -function getFirstMatchingArg(key: string) { - return Object.keys(allArgOptions).find( - (arg) => arg.replaceAll("-", "") === key.toLowerCase(), - ); -} - -const defaultValues = new Map([["access", "public"]]); - -export function createRerunSuggestion(options: Partial): string { - const optionsNormalized = { - ...options, - email: undefined, - ...(options.email && { - emailGitHub: options.email.github, - emailNpm: options.email.npm, - }), - ...(options.guide - ? { - guide: options.guide.href, - guideTitle: options.guide.title, - } - : { guide: undefined }), - ...(options.logo - ? { - logo: options.logo.src, - logoAlt: options.logo.alt, - } - : { logo: undefined }), - ...(options.offline && { - skipAllContributorsApi: undefined, - skipGitHubApi: undefined, - }), - directory: createRerunDirectorySuggestion(options), - }; - - const args = Object.entries(optionsNormalized) - // Sort so the base is first, then the rest are sorted alphabetically - .sort(([a], [b]) => - a === "base" ? -1 : b === "base" ? 1 : a.localeCompare(b), - ) - // Filter out entries with an excluded key or a default or falsy value - .filter( - ([key, value]) => - getExclusions(options, optionsNormalized.base)[key as ExclusionKey] == - undefined && - !!value && - value !== defaultValues.get(key), - ) - .map(([key, value]) => { - return `--${getFirstMatchingArg(key)}${stringifyValue(value)}`; - }) - .join(" "); - - return ["npx create-typescript-app", args].filter(Boolean).join(" "); -} - -function stringifyValue( - value: boolean | string | string[] | undefined, -): string { - if (Array.isArray(value)) { - return stringifyValue(value.join(" ")); - } - - if (typeof value === "boolean" && value) { - return ""; - } - - const valueStringified = `${value}`; - - return valueStringified.includes(" ") - ? ` "${valueStringified}"` - : ` ${valueStringified}`; -} diff --git a/src/create/createWithOptions.test.ts b/src/create/createWithOptions.test.ts deleted file mode 100644 index cbb5ce483..000000000 --- a/src/create/createWithOptions.test.ts +++ /dev/null @@ -1,195 +0,0 @@ -import { Octokit } from "octokit"; -import { describe, expect, it, vi } from "vitest"; - -import { SpinnerTask } from "../shared/cli/spinners.js"; -import { doesRepositoryExist } from "../shared/doesRepositoryExist.js"; -import { Options } from "../shared/types.js"; -import { addToolAllContributors } from "../steps/addToolAllContributors.js"; -import { finalizeDependencies } from "../steps/finalizeDependencies.js"; -import { initializeGitHubRepository } from "../steps/initializeGitHubRepository/index.js"; -import { runCleanup } from "../steps/runCleanup.js"; -import { createWithOptions } from "./createWithOptions.js"; - -const optionsBase: Options = { - access: "public", - author: "Test Author", - base: "common", - description: "Test Description", - directory: "test-directory", - email: { github: "github@example.com", npm: "npm@example.com" }, - funding: "Test Funding", - keywords: ["test", "keywords"], - logo: { alt: "Test Alt", src: "test.png" }, - mode: "create", - owner: "Test Owner", - repository: "test-repo", - title: "Test Title", -}; - -const mock$$ = vi.fn(); - -const mock$ = vi.fn().mockReturnValue(mock$$); - -vi.mock("execa", () => ({ - get $() { - return mock$; - }, -})); - -const mockOctokit = new Octokit(); - -vi.mock("../shared/cli/spinners.js", () => ({ - withSpinner: async (label: string, task: SpinnerTask) => { - return await task(); - }, - withSpinners: async ( - label: string, - tasks: [string, SpinnerTask][], - ) => { - for (const [, task] of tasks) { - await task(); - } - }, -})); - -vi.mock("../steps/writing/writeStructure.js"); - -vi.mock("../steps/writeReadme/index.js"); - -vi.mock("../steps/finalizeDependencies.js"); - -vi.mock("../steps/clearLocalGitTags.js"); - -vi.mock("../steps/runCleanup.js"); - -vi.mock("../shared/doesRepositoryExist.js", () => ({ - doesRepositoryExist: vi.fn().mockResolvedValue(true), -})); - -vi.mock("../steps/initializeGitHubRepository/index.js", () => ({ - initializeGitHubRepository: vi.fn().mockResolvedValue(true), -})); - -vi.mock("../shared/getGitHubUserAsAllContributor.js", () => ({ - getGitHubUserAsAllContributor: vi.fn().mockResolvedValue({ - contributions: ["code", "doc"], - name: "Test User", - }), -})); - -vi.mock("../steps/addToolAllContributors.js"); - -describe("createWithOptions", () => { - it("calls addToolAllContributors with options when excludeAllContributors is false", async () => { - const options = { - ...optionsBase, - excludeAllContributors: false, - skipAllContributorsApi: false, - }; - - await createWithOptions({ octokit: mockOctokit, options }, "."); - expect(addToolAllContributors).toHaveBeenCalledWith(mockOctokit, options); - }); - - it("does not call addToolAllContributors when excludeAllContributors is true", async () => { - const options = { - ...optionsBase, - excludeAllContributors: true, - }; - - await createWithOptions({ octokit: mockOctokit, options }, "."); - expect(addToolAllContributors).not.toHaveBeenCalled(); - }); - - it("does not call addToolAllContributors when skipAllContributorsApi is true", async () => { - const options = { - ...optionsBase, - skipAllContributorsApi: true, - }; - - await createWithOptions({ octokit: mockOctokit, options }, "."); - expect(addToolAllContributors).not.toHaveBeenCalled(); - }); - - it("does not call finalizeDependencies or runCleanup when skipInstall is true", async () => { - const options = { - ...optionsBase, - skipInstall: true, - }; - - await createWithOptions({ octokit: mockOctokit, options }, "."); - expect(finalizeDependencies).not.toHaveBeenCalled(); - expect(runCleanup).not.toHaveBeenCalled(); - }); - - it("calls finalizeDependencies and runCleanup when skipInstall is false", async () => { - const options = { - ...optionsBase, - skipInstall: false, - }; - - await createWithOptions({ octokit: mockOctokit, options }, "."); - - expect(finalizeDependencies).toHaveBeenCalledWith(options); - expect(runCleanup).toHaveBeenCalled(); - }); - - it("does not initialize GitHub repository if repository does not exist", async () => { - const options = optionsBase; - vi.mocked(doesRepositoryExist).mockResolvedValueOnce(false); - await createWithOptions({ octokit: mockOctokit, options }, "."); - expect(initializeGitHubRepository).not.toHaveBeenCalled(); - expect(mock$$).not.toHaveBeenCalled(); - }); - - it("executes git commands when initializing GitHub repository when doesRepositoryExist is true", async () => { - const options = optionsBase; - - vi.mocked(doesRepositoryExist).mockResolvedValueOnce(true); - await createWithOptions({ octokit: mockOctokit, options }, "."); - - expect(mock$$.mock.calls).toMatchInlineSnapshot(` - [ - [ - [ - "git remote add origin https://github.com/", - "/", - "", - ], - "Test Owner", - "test-repo", - ], - [ - [ - "git add -A", - ], - ], - [ - [ - "git commit --message ", - " --no-gpg-sign", - ], - "feat: initialized repo ✨", - ], - [ - [ - "git push -u origin main --force", - ], - ], - ] - `); - }); - - it("calls doesRepositoryExist with github and options when doesRepositoryExist is true", async () => { - const options = optionsBase; - vi.mocked(doesRepositoryExist).mockResolvedValueOnce(true); - - await createWithOptions({ octokit: mockOctokit, options }, "."); - - expect(doesRepositoryExist).toHaveBeenCalledWith(mockOctokit, options); - expect(initializeGitHubRepository).toHaveBeenCalledWith( - mockOctokit, - options, - ); - }); -}); diff --git a/src/create/createWithOptions.ts b/src/create/createWithOptions.ts deleted file mode 100644 index a87275160..000000000 --- a/src/create/createWithOptions.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { $ } from "execa"; - -import { runCreateEnginePreset } from "../next/runCreateEnginePreset.js"; -import { - LabeledSpinnerTask, - withSpinner, - withSpinners, -} from "../shared/cli/spinners.js"; -import { createCleanupCommands } from "../shared/createCleanupCommands.js"; -import { doesRepositoryExist } from "../shared/doesRepositoryExist.js"; -import { isUsingCreateEngine } from "../shared/isUsingCreateEngine.js"; -import { OctokitAndOptions } from "../shared/options/readOptions.js"; -import { addToolAllContributors } from "../steps/addToolAllContributors.js"; -import { clearLocalGitTags } from "../steps/clearLocalGitTags.js"; -import { finalizeDependencies } from "../steps/finalizeDependencies.js"; -import { initializeGitHubRepository } from "../steps/initializeGitHubRepository/index.js"; -import { runCleanup } from "../steps/runCleanup.js"; -import { writeReadme } from "../steps/writeReadme/index.js"; -import { writeStructure } from "../steps/writing/writeStructure.js"; - -export async function createWithOptions( - { octokit, options }: OctokitAndOptions, - repositoryDirectory: string, -) { - // https://github.com/JoshuaKGoldberg/create-typescript-app/pull/1781 - // I don't know why the chdir wasn't sticking from createAndEnterGitDirectory - process.chdir(repositoryDirectory); - const $$ = $({ cwd: repositoryDirectory }); - - if (isUsingCreateEngine()) { - await withSpinner("Creating repository", async () => { - await runCreateEnginePreset(options); - }); - return { sentToGitHub: false }; - } - - await withSpinners("Creating repository structure", [ - [ - "Writing structure", - async () => { - await writeStructure(options); - }, - ], - [ - "Writing README.md", - async () => { - await writeReadme(options); - }, - ] satisfies LabeledSpinnerTask, - ]); - - if (!options.excludeAllContributors && !options.skipAllContributorsApi) { - await withSpinner("Adding contributors to table", async () => { - await addToolAllContributors(octokit, options); - }); - } - - if (!options.skipInstall) { - await withSpinner("Installing packages", async () => - finalizeDependencies(options), - ); - - await runCleanup(createCleanupCommands(options.bin), options.mode); - } - - await withSpinner("Clearing any local Git tags", clearLocalGitTags); - - const sendToGitHub = octokit && (await doesRepositoryExist(octokit, options)); - - if (sendToGitHub) { - await withSpinner("Initializing GitHub repository", async () => { - await $$`git remote add origin https://github.com/${options.owner}/${options.repository}`; - await $$`git add -A`; - await $$`git commit --message ${"feat: initialized repo ✨"} --no-gpg-sign`; - await $$`git push -u origin main --force`; - await initializeGitHubRepository(octokit, options); - }); - } - - return { sentToGitHub: sendToGitHub }; -} diff --git a/src/create/index.test.ts b/src/create/index.test.ts deleted file mode 100644 index d86ec6b3f..000000000 --- a/src/create/index.test.ts +++ /dev/null @@ -1,72 +0,0 @@ -import chalk from "chalk"; -import { describe, expect, it, vi } from "vitest"; - -import { StatusCodes } from "../shared/codes.js"; -import { create } from "./index.js"; - -const mockOutro = vi.fn(); - -vi.mock("@clack/prompts", () => ({ - get outro() { - return mockOutro; - }, - spinner: vi.fn(), -})); - -const mockReadOptions = vi.fn(); - -vi.mock("../shared/options/readOptions.js", () => ({ - get readOptions() { - return mockReadOptions; - }, -})); - -const mockCreateAndEnterGitDirectory = vi.fn(); - -vi.mock("./createAndEnterGitDirectory.js", () => ({ - get createAndEnterGitDirectory() { - return mockCreateAndEnterGitDirectory; - }, -})); - -const optionsBase = { - directory: "TestDirectory", - repository: "TestRepository", -}; - -describe("create", () => { - it("returns a cancellation code when readOptions cancels", async () => { - mockReadOptions.mockResolvedValue({ - cancelled: true, - options: optionsBase, - }); - - const result = await create([]); - - expect(result).toEqual({ - code: StatusCodes.Cancelled, - options: optionsBase, - }); - }); - - it("returns a failure code when createAndEnterGitDirectory returns false", async () => { - mockReadOptions.mockResolvedValue({ - cancelled: false, - options: optionsBase, - }); - - mockCreateAndEnterGitDirectory.mockResolvedValue(false); - - const result = await create([]); - - expect(result).toEqual({ - code: StatusCodes.Failure, - options: optionsBase, - }); - expect(mockOutro).toHaveBeenCalledWith( - chalk.red( - "The TestDirectory directory already exists and is not empty. Please clear the directory, run with --mode initialize, or try a different directory.", - ), - ); - }); -}); diff --git a/src/create/index.ts b/src/create/index.ts deleted file mode 100644 index 5d1ce6610..000000000 --- a/src/create/index.ts +++ /dev/null @@ -1,74 +0,0 @@ -import * as prompts from "@clack/prompts"; -import chalk from "chalk"; - -import { outro } from "../shared/cli/outro.js"; -import { StatusCodes } from "../shared/codes.js"; -import { generateNextSteps } from "../shared/generateNextSteps.js"; -import { readOptions } from "../shared/options/readOptions.js"; -import { runOrRestore } from "../shared/runOrRestore.js"; -import { ModeRunner } from "../shared/types.js"; -import { createAndEnterGitDirectory } from "./createAndEnterGitDirectory.js"; -import { createRerunSuggestion } from "./createRerunSuggestion.js"; -import { createWithOptions } from "./createWithOptions.js"; - -export const create: ModeRunner = async (args, promptedOptions) => { - const inputs = await readOptions(args, "create", promptedOptions); - if (inputs.cancelled) { - return { - code: StatusCodes.Cancelled, - error: inputs.error, - options: inputs.options, - }; - } - - const repositoryDirectory = await createAndEnterGitDirectory( - inputs.options.directory, - ); - if (!repositoryDirectory) { - prompts.outro( - chalk.red( - `The ${inputs.options.directory} directory already exists and is not empty. Please clear the directory, run with --mode initialize, or try a different directory.`, - ), - ); - return { code: StatusCodes.Failure, options: inputs.options }; - } - - return { - code: await runOrRestore({ - run: async () => { - const { sentToGitHub } = await createWithOptions( - inputs, - repositoryDirectory, - ); - const nextSteps = generateNextSteps(inputs.options); - - outro( - sentToGitHub - ? nextSteps - : [ - { - label: - "Consider creating a GitHub repository from the new directory:", - lines: [ - `cd ${inputs.options.repository}`, - createRerunSuggestion({ - ...inputs.options, - mode: "initialize", - skipGitHubApi: false, - skipInstall: false, - }), - `git add -A`, - `git commit -m "feat: initial commit ✨"`, - `git push -u origin main`, - ], - variant: "code", - }, - ...nextSteps, - ], - ); - }, - skipRestore: inputs.options.skipRestore, - }), - options: inputs.options, - }; -}; diff --git a/src/next/blocks/packageData.test.ts b/src/data/packageData.test.ts similarity index 95% rename from src/next/blocks/packageData.test.ts rename to src/data/packageData.test.ts index 14eafebdd..17a0dc58f 100644 --- a/src/next/blocks/packageData.test.ts +++ b/src/data/packageData.test.ts @@ -13,7 +13,7 @@ vi.mock("node:module", () => ({ }), })); -describe("packageData", () => { +describe("getPackageDependencies", () => { it("returns a devDependency when it exists", () => { const actual = getPackageDependencies("package-dev-dep"); diff --git a/src/next/blocks/packageData.ts b/src/data/packageData.ts similarity index 68% rename from src/next/blocks/packageData.ts rename to src/data/packageData.ts index a72bf3adc..14d7bc033 100644 --- a/src/next/blocks/packageData.ts +++ b/src/data/packageData.ts @@ -1,4 +1,10 @@ -import { sourcePackageJson } from "./sourcePackageJson.js"; +import { createRequire } from "node:module"; + +const require = createRequire(import.meta.url); + +export const packageData = + // Importing from above src/ would expand the TS build rootDir + require("../../package.json") as typeof import("../../package.json"); export function getPackageDependencies(...names: string[]) { return Object.fromEntries( @@ -26,7 +32,7 @@ function getPackageInner( key: "dependencies" | "devDependencies", name: string, ) { - const inner = sourcePackageJson[key]; + const inner = packageData[key]; return inner[name as keyof typeof inner] as string | undefined; } diff --git a/src/docs.test.ts b/src/docs.test.ts new file mode 100644 index 000000000..19fabd8fd --- /dev/null +++ b/src/docs.test.ts @@ -0,0 +1,74 @@ +import { Block } from "create"; +import * as fs from "node:fs/promises"; +import * as prettier from "prettier"; +import { describe, expect, test } from "vitest"; + +import { blocks, presets } from "./index.js"; + +const actualLines = await createActualLines(); +const expectedLines = await createExpectedLines(); + +describe("Blocks.md", () => { + for (const [i, line] of expectedLines.entries()) { + const name = line.split(" | ")[0].replace("| ", "").trim(); + if (!name) { + continue; + } + + // eslint-disable-next-line vitest/valid-title + test(name, () => { + const actualLine = actualLines.find((line) => line.includes(`| ${name}`)); + const expectedLine = expectedLines[i]; + + expect(actualLine).toBe(expectedLine); + }); + } +}); + +async function createActualLines() { + const actualFile = (await fs.readFile("docs/Blocks.md")).toString(); + + actualFile + .split("\n") + .filter((line) => !line.includes("----")) + .map((line) => line.toLowerCase()); + + return splitTable(actualFile); +} + +async function createExpectedLines() { + const lines = [ + "| Block | Exclusion Flag | Minimal | Common | Everything |", + "| ---------------------------- | ---------------------------------------- | ------- | ------ | ---------- |", + ]; + + for (const block of Object.values(blocks) as Block[]) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const name = block.about!.name!; + + lines.push( + [ + name, + `\`--exclude-${name.replaceAll(/\W+/g, "-").toLowerCase()}\``, + presets.minimal.blocks.includes(block) ? "✔️" : " ", + presets.common.blocks.includes(block) ? "✅" : " ", + presets.everything.blocks.includes(block) ? "💯" : " ", + "", + ].join(" | "), + ); + } + + const expectedTable = await prettier.format(lines.join("\n"), { + parser: "markdown", + useTabs: true, + }); + + return splitTable(expectedTable); +} + +function splitTable(table: string) { + return table + .split("\n") + .filter((line) => !line.includes("----")) + .map((line) => line.toLowerCase()); +} diff --git a/src/greet.test.ts b/src/greet.test.ts deleted file mode 100644 index f729115fc..000000000 --- a/src/greet.test.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; - -import { greet } from "./greet.js"; - -const message = "Yay, testing!"; - -describe("greet", () => { - it("logs to the console once when message is provided as a string", () => { - const logger = vi.spyOn(console, "log").mockImplementation(() => undefined); - - greet(message); - - expect(logger).toHaveBeenCalledWith(message); - expect(logger).toHaveBeenCalledTimes(1); - }); - - it("logs to the console once when message is provided as an object", () => { - const logger = vi.spyOn(console, "log").mockImplementation(() => undefined); - - greet({ message }); - - expect(logger).toHaveBeenCalledWith(message); - expect(logger).toHaveBeenCalledTimes(1); - }); - - it("logs once when times is not provided in an object", () => { - const logger = vi.fn(); - - greet({ logger, message }); - - expect(logger).toHaveBeenCalledWith(message); - expect(logger).toHaveBeenCalledTimes(1); - }); - - it("logs a specified number of times when times is provided", () => { - const logger = vi.fn(); - const times = 7; - - greet({ logger, message, times }); - - expect(logger).toHaveBeenCalledWith(message); - expect(logger).toHaveBeenCalledTimes(7); - }); -}); diff --git a/src/greet.ts b/src/greet.ts deleted file mode 100644 index a0d3b4c67..000000000 --- a/src/greet.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { GreetOptions } from "./types.js"; - -export function greet(options: GreetOptions | string) { - const { - logger = console.log.bind(console), - message, - times = 1, - } = typeof options === "string" ? { message: options } : options; - - for (let i = 0; i < times; i += 1) { - logger(message); - } -} diff --git a/src/index.test.ts b/src/index.test.ts new file mode 100644 index 000000000..01fc1a2f8 --- /dev/null +++ b/src/index.test.ts @@ -0,0 +1,16 @@ +import { produceBase, producePreset } from "create"; +import { describe, test } from "vitest"; + +import { base, BaseOptions, presets } from "./index.js"; + +describe("index", () => { + test("end-to-end", async () => { + const created = await producePreset(presets.everything, { + options: (await produceBase(base)) as BaseOptions, + }); + + const actual = await readFilesForCreate("."); + + console.log(created); + }); +}); diff --git a/src/index.ts b/src/index.ts index 39b62f22e..d2fbcef84 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,9 +1,4 @@ -export * from "./greet.js"; - -// If you're using create-typescript-app as a template, ignore these. -// They're plumbing for the create engine. :) -export * from "./next/blocks/index.js"; -export * from "./next/presets/index.js"; -export { default } from "./next/template.js"; - -export * from "./types.js"; +export * from "./base.js"; +export * from "./blocks/index.js"; +export * from "./presets/index.js"; +export { default } from "./template.js"; diff --git a/src/initialize/index.test.ts b/src/initialize/index.test.ts deleted file mode 100644 index 703dcdd83..000000000 --- a/src/initialize/index.test.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; - -import { StatusCodes } from "../shared/codes.js"; -import { RunOrRestoreOptions } from "../shared/runOrRestore.js"; -import { initialize } from "./index.js"; - -const mockOutro = vi.fn(); - -vi.mock("../shared/cli/outro.js", () => ({ - get outro() { - return mockOutro; - }, -})); - -const mockEnsureGitRepository = vi.fn(); - -vi.mock("../shared/ensureGitRepository.js", () => ({ - get ensureGitRepository() { - return mockEnsureGitRepository; - }, -})); - -const mockReadOptions = vi.fn(); - -vi.mock("../shared/options/readOptions.js", () => ({ - get readOptions() { - return mockReadOptions; - }, -})); - -vi.mock("../shared/runOrRestore.js", () => ({ - async runOrRestore({ run }: RunOrRestoreOptions) { - await run(); - return StatusCodes.Success; - }, -})); - -const mockInitializeWithOptions = vi.fn(); - -vi.mock("./initializeWithOptions.js", () => ({ - get initializeWithOptions() { - return mockInitializeWithOptions; - }, -})); - -const optionsBase = { - repository: "TestRepository", -}; - -describe("initialize", () => { - it("returns a cancellation code when readOptions cancels", async () => { - mockReadOptions.mockResolvedValue({ - cancelled: true, - options: optionsBase, - }); - - const result = await initialize([]); - - expect(result).toEqual({ - code: StatusCodes.Cancelled, - options: optionsBase, - }); - expect(mockEnsureGitRepository).not.toHaveBeenCalled(); - }); - - it("runs initializeWithOptions when readOptions returns inputs", async () => { - mockReadOptions.mockResolvedValue({ - cancelled: false, - options: optionsBase, - }); - - const result = await initialize([]); - - expect(result).toEqual({ - code: StatusCodes.Success, - options: optionsBase, - }); - expect(mockEnsureGitRepository).toHaveBeenCalled(); - expect(mockOutro.mock.calls).toMatchInlineSnapshot(` - [ - [ - [ - { - "label": "You may consider committing these changes:", - "lines": [ - "git add -A", - "git commit -m "feat: initialized repo ✨", - "git push", - ], - "variant": "code", - }, - { - "label": "Be sure to:", - "lines": [ - "- enable the GitHub apps: - - Codecov (https://github.com/apps/codecov) - - Renovate (https://github.com/apps/renovate)", - "- populate the secrets: - - ACCESS_TOKEN (a GitHub PAT with repo and workflow permissions) - - NPM_TOKEN (an npm access token with automation permissions)", - ], - }, - ], - ], - ] - `); - }); -}); diff --git a/src/initialize/index.ts b/src/initialize/index.ts deleted file mode 100644 index a2b4caa3e..000000000 --- a/src/initialize/index.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { outro } from "../shared/cli/outro.js"; -import { StatusCodes } from "../shared/codes.js"; -import { ensureGitRepository } from "../shared/ensureGitRepository.js"; -import { generateNextSteps } from "../shared/generateNextSteps.js"; -import { readOptions } from "../shared/options/readOptions.js"; -import { runOrRestore } from "../shared/runOrRestore.js"; -import { ModeRunner } from "../shared/types.js"; -import { initializeWithOptions } from "./initializeWithOptions.js"; - -export const initialize: ModeRunner = async (args) => { - const inputs = await readOptions(args, "initialize"); - if (inputs.cancelled) { - return { - code: StatusCodes.Cancelled, - error: inputs.error, - options: inputs.options, - }; - } - - await ensureGitRepository(); - - return { - code: await runOrRestore({ - run: async () => { - await initializeWithOptions(inputs); - - outro([ - { - label: "You may consider committing these changes:", - lines: [ - `git add -A`, - `git commit -m "feat: initialized repo ✨`, - `git push`, - ], - variant: "code", - }, - ...generateNextSteps(inputs.options), - ]); - }, - skipRestore: inputs.options.skipRestore, - }), - options: inputs.options, - }; -}; diff --git a/src/initialize/initializeWithOptions.test.ts b/src/initialize/initializeWithOptions.test.ts deleted file mode 100644 index 3910a7c00..000000000 --- a/src/initialize/initializeWithOptions.test.ts +++ /dev/null @@ -1,188 +0,0 @@ -import { Octokit } from "octokit"; -import { describe, expect, it, vi } from "vitest"; - -import { Options } from "../shared/types.js"; -import { initializeWithOptions } from "./initializeWithOptions.js"; - -vi.mock("../shared/cli/spinners.js", () => ({ - async withSpinner(_label: string, task: () => Promise) { - await task(); - }, - withSpinners: vi.fn(), -})); - -const mockAddOwnerAsAllContributor = vi.fn(); - -vi.mock("../steps/addOwnerAsAllContributor.js", () => ({ - get addOwnerAsAllContributor() { - return mockAddOwnerAsAllContributor; - }, -})); - -const mockClearChangelog = vi.fn(); - -vi.mock("../steps/clearChangelog.js", () => ({ - get clearChangelog() { - return mockClearChangelog; - }, -})); - -const mockInitializeGitHubRepository = vi.fn(); - -vi.mock("../steps/initializeGitHubRepository/index.js", () => ({ - get initializeGitHubRepository() { - return mockInitializeGitHubRepository; - }, -})); - -const mockRemoveSetupScripts = vi.fn(); - -vi.mock("../steps/removeSetupScripts.js", () => ({ - get removeSetupScripts() { - return mockRemoveSetupScripts; - }, -})); - -const mockResetGitTags = vi.fn(); - -vi.mock("../steps/resetGitTags.js", () => ({ - get resetGitTags() { - return mockResetGitTags; - }, -})); - -const mockRunCleanup = vi.fn(); - -vi.mock("../steps/runCleanup.js", () => ({ - get runCleanup() { - return mockRunCleanup; - }, -})); - -const mockUninstallPackages = vi.fn(); - -vi.mock("../steps/uninstallPackages.js", () => ({ - get uninstallPackages() { - return mockUninstallPackages; - }, -})); - -const optionsBase = {} as Options; - -describe("initializeWithOptions", () => { - it("runs addOwnerAsAllContributor when excludeAllContributors is false", async () => { - const options = { - ...optionsBase, - excludeAllContributors: false, - }; - - await initializeWithOptions({ - octokit: undefined, - options, - }); - - expect(mockAddOwnerAsAllContributor).toHaveBeenCalledWith( - undefined, - options, - ); - }); - - it("does not run addOwnerAsAllContributor when excludeAllContributors is true", async () => { - const options = { - ...optionsBase, - excludeAllContributors: true, - }; - - await initializeWithOptions({ - octokit: undefined, - options, - }); - - expect(mockAddOwnerAsAllContributor).not.toHaveBeenCalled(); - }); - - it("runs initializeGitHubRepository when github is truthy", async () => { - const octokit = {} as Octokit; - - await initializeWithOptions({ - octokit, - options: optionsBase, - }); - - expect(mockInitializeGitHubRepository).toHaveBeenCalledWith( - octokit, - optionsBase, - ); - }); - - it("does not run initializeGitHubRepository when github is falsy", async () => { - const options = { - ...optionsBase, - excludeAllContributors: true, - }; - - await initializeWithOptions({ - octokit: undefined, - options, - }); - - expect(mockInitializeGitHubRepository).not.toHaveBeenCalled(); - }); - - it("runs removeSetupScripts when skipRemoval is false", async () => { - const options = { - ...optionsBase, - skipRemoval: false, - }; - - await initializeWithOptions({ - octokit: undefined, - options, - }); - - expect(mockRemoveSetupScripts).toHaveBeenCalled(); - }); - - it("does not run removeSetupScripts when skipRemoval is true", async () => { - const options = { - ...optionsBase, - skipRemoval: true, - }; - - await initializeWithOptions({ - octokit: undefined, - options, - }); - - expect(mockRemoveSetupScripts).not.toHaveBeenCalled(); - }); - - it("runs uninstallPackages when skipUninstall is false", async () => { - const options = { - ...optionsBase, - offline: true, - skipUninstall: false, - }; - - await initializeWithOptions({ - octokit: undefined, - options, - }); - - expect(mockUninstallPackages).toHaveBeenCalledWith(options.offline); - }); - - it("does not run uninstallPackages when skipUninstall is true", async () => { - const options = { - ...optionsBase, - skipUninstall: true, - }; - - await initializeWithOptions({ - octokit: undefined, - options, - }); - - expect(mockUninstallPackages).not.toHaveBeenCalled(); - }); -}); diff --git a/src/initialize/initializeWithOptions.ts b/src/initialize/initializeWithOptions.ts deleted file mode 100644 index f6e37cbf6..000000000 --- a/src/initialize/initializeWithOptions.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { withSpinner, withSpinners } from "../shared/cli/spinners.js"; -import { createCleanupCommands } from "../shared/createCleanupCommands.js"; -import { OctokitAndOptions } from "../shared/options/readOptions.js"; -import { addOwnerAsAllContributor } from "../steps/addOwnerAsAllContributor.js"; -import { clearChangelog } from "../steps/clearChangelog.js"; -import { initializeGitHubRepository } from "../steps/initializeGitHubRepository/index.js"; -import { removeSetupScripts } from "../steps/removeSetupScripts.js"; -import { resetGitTags } from "../steps/resetGitTags.js"; -import { runCleanup } from "../steps/runCleanup.js"; -import { uninstallPackages } from "../steps/uninstallPackages.js"; -import { updateAllContributorsTable } from "../steps/updateAllContributorsTable.js"; -import { updateLocalFiles } from "../steps/updateLocalFiles.js"; -import { updateReadme } from "../steps/updateReadme.js"; - -export async function initializeWithOptions({ - octokit, - options, -}: OctokitAndOptions) { - await withSpinners("Initializing local files", [ - [ - "Updating local files", - async () => { - await updateLocalFiles(options); - }, - ], - [ - "Updating README.md", - async () => { - await updateReadme(options); - }, - ], - ["Clearing changelog", clearChangelog], - [ - "Updating all-contributors table", - async () => { - await updateAllContributorsTable(options); - }, - ], - ["Resetting Git tags", resetGitTags], - ]); - - if (!options.excludeAllContributors) { - await withSpinner("Updating existing contributor details", async () => { - await addOwnerAsAllContributor(octokit, options); - }); - } - - if (octokit) { - await withSpinner("Initializing GitHub repository", async () => { - await initializeGitHubRepository(octokit, options); - }); - } - - if (!options.skipRemoval) { - await withSpinner("Removing setup scripts", removeSetupScripts); - } - - if (!options.skipUninstall) { - await withSpinner("Uninstalling initialization-only packages", async () => - uninstallPackages(options.offline), - ); - } - - await runCleanup( - createCleanupCommands(options.bin, "pnpm dedupe --offline"), - options.mode, - ); -} diff --git a/src/migrate/index.test.ts b/src/migrate/index.test.ts deleted file mode 100644 index c13782e55..000000000 --- a/src/migrate/index.test.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; - -import { StatusCodes } from "../shared/codes.js"; -import { RunOrRestoreOptions } from "../shared/runOrRestore.js"; -import { migrate } from "./index.js"; - -const mockOutro = vi.fn(); - -vi.mock("../shared/cli/outro.js", () => ({ - get outro() { - return mockOutro; - }, -})); - -const mockEnsureGitRepository = vi.fn(); - -vi.mock("../shared/ensureGitRepository.js", () => ({ - get ensureGitRepository() { - return mockEnsureGitRepository; - }, -})); - -const mockReadOptions = vi.fn(); - -vi.mock("../shared/options/readOptions.js", () => ({ - get readOptions() { - return mockReadOptions; - }, -})); - -vi.mock("../shared/runOrRestore.js", () => ({ - async runOrRestore({ run }: RunOrRestoreOptions) { - await run(); - return StatusCodes.Success; - }, -})); - -const mockMigrateWithOptions = vi.fn(); - -vi.mock("./migrateWithOptions.js", () => ({ - get migrateWithOptions() { - return mockMigrateWithOptions; - }, -})); - -const optionsBase = { - repository: "TestRepository", -}; - -describe("migrate", () => { - it("returns a cancellation code when readOptions cancels", async () => { - mockReadOptions.mockResolvedValue({ - cancelled: true, - options: optionsBase, - }); - - const result = await migrate([]); - - expect(result).toEqual({ - code: StatusCodes.Cancelled, - options: optionsBase, - }); - }); - - it("runs migrateWithOptions when readOptions returns inputs", async () => { - mockReadOptions.mockResolvedValue({ - cancelled: false, - options: optionsBase, - }); - - const result = await migrate([]); - - expect(result).toEqual({ - code: StatusCodes.Success, - options: optionsBase, - }); - expect(mockEnsureGitRepository).toHaveBeenCalled(); - expect(mockOutro.mock.calls).toMatchInlineSnapshot(` - [ - [ - [ - { - "label": "You may consider committing these changes:", - "lines": [ - "git add -A", - "git commit -m "migrated repo to create-typescript-app ✨", - "git push", - ], - "variant": "code", - }, - { - "label": "Be sure to:", - "lines": [ - "- enable the GitHub apps: - - Codecov (https://github.com/apps/codecov) - - Renovate (https://github.com/apps/renovate)", - "- populate the secrets: - - ACCESS_TOKEN (a GitHub PAT with repo and workflow permissions) - - NPM_TOKEN (an npm access token with automation permissions)", - ], - }, - ], - ], - ] - `); - }); -}); diff --git a/src/migrate/index.ts b/src/migrate/index.ts deleted file mode 100644 index 7f4fd02ed..000000000 --- a/src/migrate/index.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { outro } from "../shared/cli/outro.js"; -import { StatusCodes } from "../shared/codes.js"; -import { ensureGitRepository } from "../shared/ensureGitRepository.js"; -import { generateNextSteps } from "../shared/generateNextSteps.js"; -import { readOptions } from "../shared/options/readOptions.js"; -import { runOrRestore } from "../shared/runOrRestore.js"; -import { ModeRunner } from "../shared/types.js"; -import { migrateWithOptions } from "./migrateWithOptions.js"; - -export const migrate: ModeRunner = async (args) => { - const inputs = await readOptions(args, "migrate"); - if (inputs.cancelled) { - return { - code: StatusCodes.Cancelled, - error: inputs.error, - options: inputs.options, - }; - } - - await ensureGitRepository(); - - return { - code: await runOrRestore({ - run: async () => { - await migrateWithOptions(inputs); - - outro([ - { - label: "You may consider committing these changes:", - lines: [ - `git add -A`, - `git commit -m "migrated repo to create-typescript-app ✨`, - `git push`, - ], - variant: "code", - }, - ...generateNextSteps(inputs.options), - ]); - }, - skipRestore: inputs.options.skipRestore, - }), - options: inputs.options, - }; -}; diff --git a/src/migrate/migrateWithOptions.test.ts b/src/migrate/migrateWithOptions.test.ts deleted file mode 100644 index 5215b27a9..000000000 --- a/src/migrate/migrateWithOptions.test.ts +++ /dev/null @@ -1,199 +0,0 @@ -import { Octokit } from "octokit"; -import { describe, expect, it, vi } from "vitest"; - -import { Options } from "../shared/types.js"; -import { migrateWithOptions } from "./migrateWithOptions.js"; - -vi.mock("../shared/cli/spinners.js", () => ({ - async withSpinner(_label: string, task: () => Promise) { - await task(); - }, - withSpinners: vi.fn(), -})); - -const mockPopulateAllContributorsForRepository = vi.fn(); - -vi.mock("populate-all-contributors-for-repository", () => ({ - get populateAllContributorsForRepository() { - return mockPopulateAllContributorsForRepository; - }, -})); - -const mockFinalizeDependencies = vi.fn(); - -vi.mock("../steps/finalizeDependencies.js", () => ({ - get finalizeDependencies() { - return mockFinalizeDependencies; - }, -})); - -const mockInitializeGitHubRepository = vi.fn(); - -vi.mock("../steps/initializeGitHubRepository/index.js", () => ({ - get initializeGitHubRepository() { - return mockInitializeGitHubRepository; - }, -})); - -const mockPopulateCSpellDictionary = vi.fn(); - -vi.mock("../steps/populateCSpellDictionary.js", () => ({ - get populateCSpellDictionary() { - return mockPopulateCSpellDictionary; - }, -})); - -vi.mock("../steps/runCleanup.js", () => ({ - runCleanup: vi.fn(), -})); - -vi.mock("../shared/cli/spinners.js", () => ({ - async withSpinner(_label: string, task: () => Promise) { - await task(); - }, - withSpinners: vi.fn(), -})); - -const mockGitHubAndOptions = vi.fn(); - -vi.mock("../shared/options/readOptions.js", () => ({ - get GitHubAndOptions() { - return mockGitHubAndOptions; - }, -})); - -const optionsBase = {} as Options; - -describe("migrateWithOptions", () => { - it("runs initializeGitHubRepository when octokit is truthy", async () => { - const octokit = {} as Octokit; - - await migrateWithOptions({ - octokit, - options: optionsBase, - }); - - expect(mockInitializeGitHubRepository).toHaveBeenCalledWith( - octokit, - optionsBase, - ); - }); - - it("does not run initializeGitHubRepository when github is falsy", async () => { - const options = { - ...optionsBase, - excludeAllContributors: true, - }; - - await migrateWithOptions({ - octokit: undefined, - options, - }); - - expect(mockInitializeGitHubRepository).not.toHaveBeenCalled(); - }); - - it("does not run populateAllContributorsForRepository excludeAllContributors is true", async () => { - const options = { - ...optionsBase, - excludeAllContributors: true, - skipAllContributorsApi: false, - }; - - await migrateWithOptions({ - octokit: undefined, - options, - }); - - expect(mockPopulateAllContributorsForRepository).not.toHaveBeenCalled(); - }); - - it("does not run populateAllContributorsForRepository skipAllContributorsApi is true", async () => { - const options = { - ...optionsBase, - excludeAllContributors: false, - skipAllContributorsApi: true, - }; - - await migrateWithOptions({ - octokit: undefined, - options, - }); - - expect(mockPopulateAllContributorsForRepository).not.toHaveBeenCalled(); - }); - - it("runs populateAllContributorsForRepository excludeAllContributors and skipAllContributorsApi are false", async () => { - const options = { - ...optionsBase, - excludeAllContributors: false, - skipAllContributorsApi: false, - }; - - await migrateWithOptions({ - octokit: undefined, - options, - }); - - expect(mockPopulateAllContributorsForRepository).toHaveBeenCalledWith({ - owner: options.owner, - repo: options.repository, - }); - }); - - it("runs finalizeDependencies when skipInstall is false", async () => { - const options = { - ...optionsBase, - skipInstall: false, - }; - - await migrateWithOptions({ - octokit: undefined, - options, - }); - - expect(mockFinalizeDependencies).toHaveBeenCalledWith(options); - }); - - it("does not run finalizeDependencies when skipInstall is true", async () => { - const options = { - ...optionsBase, - skipInstall: true, - }; - - await migrateWithOptions({ - octokit: undefined, - options, - }); - - expect(mockFinalizeDependencies).not.toHaveBeenCalled(); - }); - - it("runs populateCSpellDictionary when excludeLintSpelling is false", async () => { - const options = { - ...optionsBase, - excludeLintSpelling: false, - }; - - await migrateWithOptions({ - octokit: undefined, - options, - }); - - expect(mockPopulateCSpellDictionary).toHaveBeenCalled(); - }); - - it("does not run populateCSpellDictionary when excludeLintSpelling is true", async () => { - const options = { - ...optionsBase, - excludeLintSpelling: true, - }; - - await migrateWithOptions({ - octokit: undefined, - options, - }); - - expect(mockPopulateCSpellDictionary).not.toHaveBeenCalled(); - }); -}); diff --git a/src/migrate/migrateWithOptions.ts b/src/migrate/migrateWithOptions.ts deleted file mode 100644 index 00ea83c6f..000000000 --- a/src/migrate/migrateWithOptions.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { populateAllContributorsForRepository } from "populate-all-contributors-for-repository"; - -import { withSpinner, withSpinners } from "../shared/cli/spinners.js"; -import { createCleanupCommands } from "../shared/createCleanupCommands.js"; -import { OctokitAndOptions } from "../shared/options/readOptions.js"; -import { clearUnnecessaryFiles } from "../steps/clearUnnecessaryFiles.js"; -import { finalizeDependencies } from "../steps/finalizeDependencies.js"; -import { initializeGitHubRepository } from "../steps/initializeGitHubRepository/index.js"; -import { populateCSpellDictionary } from "../steps/populateCSpellDictionary.js"; -import { runCleanup } from "../steps/runCleanup.js"; -import { updateAllContributorsTable } from "../steps/updateAllContributorsTable.js"; -import { updateLocalFiles } from "../steps/updateLocalFiles.js"; -import { writeReadme } from "../steps/writeReadme/index.js"; -import { writeStructure } from "../steps/writing/writeStructure.js"; - -export async function migrateWithOptions({ - octokit, - options, -}: OctokitAndOptions) { - await withSpinners("Migrating repository structure", [ - ["Clearing unnecessary files", clearUnnecessaryFiles], - [ - "Writing structure", - async () => { - await writeStructure(options); - }, - ], - [ - "Writing README.md", - async () => { - await writeReadme(options); - }, - ], - [ - "Updating local files", - async () => { - await updateLocalFiles(options); - }, - ], - [ - "Updating all-contributors table", - async () => { - await updateAllContributorsTable(options); - }, - ], - ]); - - if (octokit) { - await withSpinner("Initializing GitHub repository", async () => { - await initializeGitHubRepository(octokit, options); - }); - } - - if (!options.excludeAllContributors && !options.skipAllContributorsApi) { - await withSpinner("Detecting existing contributors", async () => - populateAllContributorsForRepository({ - owner: options.owner, - repo: options.repository, - }), - ); - } - - if (!options.skipInstall) { - await withSpinner("Installing packages", async () => - finalizeDependencies(options), - ); - } - - if (!options.excludeLintSpelling) { - await withSpinner("Populating CSpell dictionary", populateCSpellDictionary); - } - - await runCleanup(createCleanupCommands(options.bin), options.mode); -} diff --git a/src/next/blocks/index.ts b/src/next/blocks/index.ts deleted file mode 100644 index 26d79f044..000000000 --- a/src/next/blocks/index.ts +++ /dev/null @@ -1,42 +0,0 @@ -export * from "./blockAllContributors.js"; -export * from "./blockAreTheTypesWrong.js"; -export * from "./blockContributingDocs.js"; -export * from "./blockContributorCovenant.js"; -export * from "./blockCSpell.js"; -export * from "./blockDevelopmentDocs.js"; -export * from "./blockESLint.js"; -export * from "./blockESLintComments.js"; -export * from "./blockESLintJSDoc.js"; -export * from "./blockESLintJSONC.js"; -export * from "./blockESLintMarkdown.js"; -export * from "./blockESLintMoreStyling.js"; -export * from "./blockESLintNode.js"; -export * from "./blockESLintPackageJson.js"; -export * from "./blockESLintPerfectionist.js"; -export * from "./blockESLintRegexp.js"; -export * from "./blockESLintYML.js"; -export * from "./blockFunding.js"; -export * from "./blockGitHubActionsCI.js"; -export * from "./blockGitHubIssueTemplates.js"; -export * from "./blockGitHubPRTemplate.js"; -export * from "./blockGitignore.js"; -export * from "./blockKnip.js"; -export * from "./blockMarkdownlint.js"; -export * from "./blockMITLicense.js"; -export * from "./blockNvmrc.js"; -export * from "./blockPackageJson.js"; -export * from "./blockPnpmDedupe.js"; -export * from "./blockPRCompliance.js"; -export * from "./blockPrettier.js"; -export * from "./blockPrettierPluginCurly.js"; -export * from "./blockPrettierPluginPackageJson.js"; -export * from "./blockPrettierPluginSh.js"; -export * from "./blockREADME.js"; -export * from "./blockReleaseIt.js"; -export * from "./blockRenovate.js"; -export * from "./blockSecurityDocs.js"; -export * from "./blockTemplatedBy.js"; -export * from "./blockTSup.js"; -export * from "./blockTypeScript.js"; -export * from "./blockVitest.js"; -export * from "./blockVSCode.js"; diff --git a/src/next/blocks/sourcePackageJson.ts b/src/next/blocks/sourcePackageJson.ts deleted file mode 100644 index 75867f05d..000000000 --- a/src/next/blocks/sourcePackageJson.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { createRequire } from "node:module"; - -const require = createRequire(import.meta.url); - -export const sourcePackageJson = - // Importing from above src/ would expand the TS build rootDir - require("../../../package.json") as typeof import("../../../package.json"); diff --git a/src/next/presets/index.ts b/src/next/presets/index.ts deleted file mode 100644 index d995968ea..000000000 --- a/src/next/presets/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from "./presetCommon.js"; -export * from "./presetEverything.js"; -export * from "./presetMinimal.js"; diff --git a/src/next/runCreateEnginePreset.ts b/src/next/runCreateEnginePreset.ts deleted file mode 100644 index 30196a235..000000000 --- a/src/next/runCreateEnginePreset.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { runPreset } from "create"; - -import { Options } from "../shared/types.js"; -import { presetCommon } from "./presets/presetCommon.js"; -import { presetEverything } from "./presets/presetEverything.js"; -import { presetMinimal } from "./presets/presetMinimal.js"; - -const presets = { - common: presetCommon, - everything: presetEverything, - minimal: presetMinimal, -}; - -export async function runCreateEnginePreset(options: Options) { - const preset = - options.base && options.base !== "prompt" && presets[options.base]; - - if (!preset) { - throw new Error(`Cannot yet use create engine with base ${options.base}.`); - } - - await runPreset(preset, { mode: "initialize", options }); -} diff --git a/src/shared/options/createOptionDefaults/getUsageFromReadme.test.ts b/src/options/getUsageFromReadme.test.ts similarity index 100% rename from src/shared/options/createOptionDefaults/getUsageFromReadme.test.ts rename to src/options/getUsageFromReadme.test.ts diff --git a/src/shared/options/createOptionDefaults/getUsageFromReadme.ts b/src/options/getUsageFromReadme.ts similarity index 100% rename from src/shared/options/createOptionDefaults/getUsageFromReadme.ts rename to src/options/getUsageFromReadme.ts diff --git a/src/shared/options/createOptionDefaults/parsePackageAuthor.test.ts b/src/options/parsePackageAuthor.test.ts similarity index 75% rename from src/shared/options/createOptionDefaults/parsePackageAuthor.test.ts rename to src/options/parsePackageAuthor.test.ts index 078768d18..931a2df94 100644 --- a/src/shared/options/createOptionDefaults/parsePackageAuthor.test.ts +++ b/src/options/parsePackageAuthor.test.ts @@ -1,15 +1,7 @@ -import { describe, expect, it, vi } from "vitest"; +import { describe, expect, it } from "vitest"; import { parsePackageAuthor } from "./parsePackageAuthor.js"; -const mockReadFileSafe = vi.fn(); - -vi.mock("../../shared/readFileSafe.js", () => ({ - get readFileSafe() { - return mockReadFileSafe; - }, -})); - describe("parsePackageAuthor", () => { it.each([ [{}, {}], diff --git a/src/shared/options/createOptionDefaults/parsePackageAuthor.ts b/src/options/parsePackageAuthor.ts similarity index 91% rename from src/shared/options/createOptionDefaults/parsePackageAuthor.ts rename to src/options/parsePackageAuthor.ts index 5fa833d19..b7ffd000a 100644 --- a/src/shared/options/createOptionDefaults/parsePackageAuthor.ts +++ b/src/options/parsePackageAuthor.ts @@ -1,6 +1,6 @@ import parse from "parse-author"; -import { PartialPackageData } from "../../types.js"; +import { PartialPackageData } from "../types.js"; export function parsePackageAuthor(packageData: PartialPackageData) { let packageAuthor: string | undefined; diff --git a/src/shared/options/createOptionDefaults/readDefaultsFromReadme.test.ts b/src/options/readDefaultsFromReadme.test.ts similarity index 98% rename from src/shared/options/createOptionDefaults/readDefaultsFromReadme.test.ts rename to src/options/readDefaultsFromReadme.test.ts index c9559ccc0..a994a776e 100644 --- a/src/shared/options/createOptionDefaults/readDefaultsFromReadme.test.ts +++ b/src/options/readDefaultsFromReadme.test.ts @@ -4,7 +4,7 @@ import { readDefaultsFromReadme } from "./readDefaultsFromReadme.js"; const mockReadLogoSizing = vi.fn().mockResolvedValue({}); -vi.mock("../readLogoSizing.js", () => ({ +vi.mock("./readLogoSizing.js", () => ({ get readLogoSizing() { return mockReadLogoSizing; }, @@ -107,7 +107,7 @@ describe("readDefaultsFromReadme", () => { const logo = await readDefaultsFromReadme( () => Promise.resolve(` -Project logo: a fancy circle`), +Project logo: a fancy circle`), () => Promise.resolve(undefined), ).logo(); diff --git a/src/shared/options/createOptionDefaults/readDefaultsFromReadme.ts b/src/options/readDefaultsFromReadme.ts similarity index 85% rename from src/shared/options/createOptionDefaults/readDefaultsFromReadme.ts rename to src/options/readDefaultsFromReadme.ts index 41fb6ed82..f27492b21 100644 --- a/src/shared/options/createOptionDefaults/readDefaultsFromReadme.ts +++ b/src/options/readDefaultsFromReadme.ts @@ -1,8 +1,8 @@ import lazyValue from "lazy-value"; import { titleCase } from "title-case"; -import { readLogoSizing } from "../readLogoSizing.js"; import { getUsageFromReadme } from "./getUsageFromReadme.js"; +import { readLogoSizing } from "./readLogoSizing.js"; export function readDefaultsFromReadme( readme: () => Promise, @@ -22,14 +22,17 @@ export function readDefaultsFromReadme( const src = /src\s*=(.+)['"/]>/ .exec(tag)?.[1] - ?.replaceAll(/^['"]|['"]$/g, ""); + ?.split(/\s*\w+=/)[0] + .replaceAll(/^['"]|['"]$/g, ""); if (!src) { return undefined; } return { - alt: /alt=['"](.+)['"]\s*src=/.exec(tag)?.[1] ?? "Project logo", + alt: + /alt=['"](.+)['"]\s*src=/.exec(tag)?.[1].split(/['"]?\s*\w+=/)[0] ?? + "Project logo", src, ...readLogoSizing(src), }; diff --git a/src/next/readDescription.test.ts b/src/options/readDescription.test.ts similarity index 79% rename from src/next/readDescription.test.ts rename to src/options/readDescription.test.ts index 8f44f250a..0fb5da8e5 100644 --- a/src/next/readDescription.test.ts +++ b/src/options/readDescription.test.ts @@ -2,12 +2,12 @@ import { describe, expect, it, vi } from "vitest"; import { readDescription } from "./readDescription.js"; -const mockSourcePackageJson = vi.fn<() => string>(); +const mockPackageDataDescription = vi.fn<() => string>(); -vi.mock("./blocks/sourcePackageJson", () => ({ - sourcePackageJson: { +vi.mock("../data/packageData.js", () => ({ + packageData: { get description() { - return mockSourcePackageJson(); + return mockPackageDataDescription(); }, }, })); @@ -16,7 +16,7 @@ describe("readDescription", () => { it("returns undefined when the description matches the current package.json description", async () => { const existing = "Same description."; - mockSourcePackageJson.mockReturnValueOnce(existing); + mockPackageDataDescription.mockReturnValueOnce(existing); const description = await readDescription( () => Promise.resolve({ description: existing }), @@ -29,7 +29,7 @@ describe("readDescription", () => { it("returns the updated description when neither description nor name match the current package.json", async () => { const updated = "Updated description."; - mockSourcePackageJson.mockReturnValueOnce("Existing description"); + mockPackageDataDescription.mockReturnValueOnce("Existing description"); const description = await readDescription( () => Promise.resolve({ description: updated }), @@ -43,7 +43,7 @@ describe("readDescription", () => { const plaintext = "Updated description."; const encoded = "Updated description."; - mockSourcePackageJson.mockReturnValueOnce("Existing description"); + mockPackageDataDescription.mockReturnValueOnce("Existing description"); const description = await readDescription( () => Promise.resolve({ description: plaintext }), @@ -57,7 +57,7 @@ describe("readDescription", () => { const plaintext = "Updated description."; const encoded = "Incorrect description."; - mockSourcePackageJson.mockReturnValueOnce("Existing description"); + mockPackageDataDescription.mockReturnValueOnce("Existing description"); const description = await readDescription( () => Promise.resolve({ description: plaintext }), diff --git a/src/next/readDescription.ts b/src/options/readDescription.ts similarity index 74% rename from src/next/readDescription.ts rename to src/options/readDescription.ts index 5caa5c53e..ced7370e6 100644 --- a/src/next/readDescription.ts +++ b/src/options/readDescription.ts @@ -1,5 +1,5 @@ -import { PartialPackageData } from "../shared/types.js"; -import { sourcePackageJson } from "./blocks/sourcePackageJson.js"; +import { packageData } from "../data/packageData.js"; +import { PartialPackageData } from "../types.js"; import { readDescriptionFromReadme } from "./readDescriptionFromReadme.js"; export async function readDescription( @@ -11,7 +11,7 @@ export async function readDescription( return undefined; } - const { description: existing } = sourcePackageJson; + const { description: existing } = packageData; const fromReadme = await readDescriptionFromReadme(getReadme); if (fromReadme?.replaceAll(/<\s*(?:\/\s*)?\w+\s*>/gu, "") === inferred) { diff --git a/src/next/readDescriptionFromReadme.test.ts b/src/options/readDescriptionFromReadme.test.ts similarity index 100% rename from src/next/readDescriptionFromReadme.test.ts rename to src/options/readDescriptionFromReadme.test.ts diff --git a/src/next/readDescriptionFromReadme.ts b/src/options/readDescriptionFromReadme.ts similarity index 100% rename from src/next/readDescriptionFromReadme.ts rename to src/options/readDescriptionFromReadme.ts diff --git a/src/next/readDocumentation.test.ts b/src/options/readDocumentation.test.ts similarity index 100% rename from src/next/readDocumentation.test.ts rename to src/options/readDocumentation.test.ts diff --git a/src/next/readDocumentation.ts b/src/options/readDocumentation.ts similarity index 92% rename from src/next/readDocumentation.ts rename to src/options/readDocumentation.ts index 5fd0d0be9..e2cac2c4d 100644 --- a/src/next/readDocumentation.ts +++ b/src/options/readDocumentation.ts @@ -1,7 +1,7 @@ import { TakeInput } from "create"; import { inputFromFile } from "input-from-file"; -import { swallowError } from "./utils/swallowError.js"; +import { swallowError } from "../utils/swallowError.js"; const knownHeadings = new Set([ "building", diff --git a/src/shared/options/createOptionDefaults/readEmails.ts b/src/options/readEmails.ts similarity index 91% rename from src/shared/options/createOptionDefaults/readEmails.ts rename to src/options/readEmails.ts index d26579e98..0fa4af380 100644 --- a/src/shared/options/createOptionDefaults/readEmails.ts +++ b/src/options/readEmails.ts @@ -1,7 +1,7 @@ import { $ } from "execa"; import { UserInfo } from "npm-user"; -import { tryCatchAsync } from "../../tryCatchAsync.js"; +import { tryCatchAsync } from "../utils/tryCatchAsync.js"; import { readGitHubEmail } from "./readGitHubEmail.js"; export async function readEmails( diff --git a/src/shared/readFileAsJson.test.ts b/src/options/readFileAsJson.test.ts similarity index 100% rename from src/shared/readFileAsJson.test.ts rename to src/options/readFileAsJson.test.ts diff --git a/src/shared/readFileAsJson.ts b/src/options/readFileAsJson.ts similarity index 100% rename from src/shared/readFileAsJson.ts rename to src/options/readFileAsJson.ts diff --git a/src/shared/readFileSafe.test.ts b/src/options/readFileSafe.test.ts similarity index 100% rename from src/shared/readFileSafe.test.ts rename to src/options/readFileSafe.test.ts diff --git a/src/shared/readFileSafe.ts b/src/options/readFileSafe.ts similarity index 100% rename from src/shared/readFileSafe.ts rename to src/options/readFileSafe.ts diff --git a/src/shared/options/createOptionDefaults/readFunding.ts b/src/options/readFunding.ts similarity index 77% rename from src/shared/options/createOptionDefaults/readFunding.ts rename to src/options/readFunding.ts index 7de124697..9d78ab08c 100644 --- a/src/shared/options/createOptionDefaults/readFunding.ts +++ b/src/options/readFunding.ts @@ -1,6 +1,6 @@ import fs from "node:fs/promises"; -import { tryCatchAsync } from "../../tryCatchAsync.js"; +import { tryCatchAsync } from "../utils/tryCatchAsync.js"; export async function readFunding() { return await tryCatchAsync(async () => diff --git a/src/shared/options/createOptionDefaults/readGitHubEmail.test.ts b/src/options/readGitHubEmail.test.ts similarity index 96% rename from src/shared/options/createOptionDefaults/readGitHubEmail.test.ts rename to src/options/readGitHubEmail.test.ts index f472abeb1..a001e7667 100644 --- a/src/shared/options/createOptionDefaults/readGitHubEmail.test.ts +++ b/src/options/readGitHubEmail.test.ts @@ -4,7 +4,7 @@ import { readGitHubEmail } from "./readGitHubEmail.js"; const mockReadFileSafe = vi.fn(); -vi.mock("../../readFileSafe.js", () => ({ +vi.mock("./readFileSafe.js", () => ({ get readFileSafe() { return mockReadFileSafe; }, diff --git a/src/shared/options/createOptionDefaults/readGitHubEmail.ts b/src/options/readGitHubEmail.ts similarity index 88% rename from src/shared/options/createOptionDefaults/readGitHubEmail.ts rename to src/options/readGitHubEmail.ts index ea52beacb..884274e4a 100644 --- a/src/shared/options/createOptionDefaults/readGitHubEmail.ts +++ b/src/options/readGitHubEmail.ts @@ -1,4 +1,4 @@ -import { readFileSafe } from "../../readFileSafe.js"; +import { readFileSafe } from "./readFileSafe.js"; export async function readGitHubEmail() { // The create-typescript-app template puts the GitHub email in the CoC. diff --git a/src/shared/options/createOptionDefaults/readGuide.test.ts b/src/options/readGuide.test.ts similarity index 96% rename from src/shared/options/createOptionDefaults/readGuide.test.ts rename to src/options/readGuide.test.ts index 9a438babc..d0fcdb761 100644 --- a/src/shared/options/createOptionDefaults/readGuide.test.ts +++ b/src/options/readGuide.test.ts @@ -4,7 +4,7 @@ import { readGuide } from "./readGuide.js"; const mockReadFileSafe = vi.fn(); -vi.mock("../../readFileSafe.js", () => ({ +vi.mock("./readFileSafe.js", () => ({ get readFileSafe() { return mockReadFileSafe; }, diff --git a/src/shared/options/createOptionDefaults/readGuide.ts b/src/options/readGuide.ts similarity index 84% rename from src/shared/options/createOptionDefaults/readGuide.ts rename to src/options/readGuide.ts index dbbf6ad03..05c44904f 100644 --- a/src/shared/options/createOptionDefaults/readGuide.ts +++ b/src/options/readGuide.ts @@ -1,4 +1,4 @@ -import { readFileSafe } from "../../readFileSafe.js"; +import { readFileSafe } from "./readFileSafe.js"; export async function readGuide() { const development = await readFileSafe(".github/DEVELOPMENT.md", ""); diff --git a/src/shared/options/readLogoSizing.test.ts b/src/options/readLogoSizing.test.ts similarity index 100% rename from src/shared/options/readLogoSizing.test.ts rename to src/options/readLogoSizing.test.ts diff --git a/src/shared/options/readLogoSizing.ts b/src/options/readLogoSizing.ts similarity index 100% rename from src/shared/options/readLogoSizing.ts rename to src/options/readLogoSizing.ts diff --git a/src/options/readPackageData.ts b/src/options/readPackageData.ts new file mode 100644 index 000000000..359c70a31 --- /dev/null +++ b/src/options/readPackageData.ts @@ -0,0 +1,10 @@ +import { PartialPackageData } from "../types.js"; +import { readFileSafe } from "./readFileSafe.js"; + +export async function readPackageData() { + return ( + (JSON.parse(await readFileSafe("./package.json", "{}")) as + | PartialPackageData + | undefined) ?? {} + ); +} diff --git a/src/next/presets/presetCommon.ts b/src/presets/common.ts similarity index 90% rename from src/next/presets/presetCommon.ts rename to src/presets/common.ts index 2f402f447..b33f738ac 100644 --- a/src/next/presets/presetCommon.ts +++ b/src/presets/common.ts @@ -2,7 +2,7 @@ import { base } from "../base.js"; import { blockAllContributors } from "../blocks/blockAllContributors.js"; import { blockReleaseIt } from "../blocks/blockReleaseIt.js"; import { blockVitest } from "../blocks/blockVitest.js"; -import { presetMinimal } from "./presetMinimal.js"; +import { presetMinimal } from "./minimal.js"; export const presetCommon = base.createPreset({ about: { diff --git a/src/next/presets/presetEverything.ts b/src/presets/everything.ts similarity index 97% rename from src/next/presets/presetEverything.ts rename to src/presets/everything.ts index 557dce519..5cff5fc53 100644 --- a/src/next/presets/presetEverything.ts +++ b/src/presets/everything.ts @@ -21,7 +21,7 @@ import { blockPrettierPluginSh } from "../blocks/blockPrettierPluginSh.js"; import { blockRenovate } from "../blocks/blockRenovate.js"; import { blockSecurityDocs } from "../blocks/blockSecurityDocs.js"; import { blockVSCode } from "../blocks/blockVSCode.js"; -import { presetCommon } from "../presets/presetCommon.js"; +import { presetCommon } from "./common.js"; export const presetEverything = base.createPreset({ about: { diff --git a/src/presets/index.ts b/src/presets/index.ts new file mode 100644 index 000000000..5b140abdb --- /dev/null +++ b/src/presets/index.ts @@ -0,0 +1,13 @@ +import { presetCommon } from "./common.js"; +import { presetEverything } from "./everything.js"; +import { presetMinimal } from "./minimal.js"; + +export const presets = { + common: presetCommon, + everything: presetEverything, + minimal: presetMinimal, +}; + +export { presetCommon } from "./common.js"; +export { presetEverything } from "./everything.js"; +export { presetMinimal } from "./minimal.js"; diff --git a/src/next/presets/presetMinimal.ts b/src/presets/minimal.ts similarity index 95% rename from src/next/presets/presetMinimal.ts rename to src/presets/minimal.ts index 587f9ea0b..0ff44447f 100644 --- a/src/next/presets/presetMinimal.ts +++ b/src/presets/minimal.ts @@ -18,7 +18,7 @@ import { blockRepositoryBranchRuleset } from "../blocks/blockRepositoryBranchRul import { blockRepositoryLabels } from "../blocks/blockRepositoryLabels.js"; import { blockRepositorySecrets } from "../blocks/blockRepositorySecrets.js"; import { blockRepositorySettings } from "../blocks/blockRepositorySettings.js"; -import { blockTemplatedBy } from "../blocks/blockTemplatedBy.js"; +import { blockTemplatedWith } from "../blocks/blockTemplatedWith.js"; import { blockTSup } from "../blocks/blockTSup.js"; import { blockTypeScript } from "../blocks/blockTypeScript.js"; @@ -48,7 +48,7 @@ export const presetMinimal = base.createPreset({ blockRepositoryLabels, blockRepositorySecrets, blockRepositorySettings, - blockTemplatedBy, + blockTemplatedWith, blockTSup, blockTypeScript, ], diff --git a/src/shared/__snapshots__/generateNextSteps.test.ts.snap b/src/shared/__snapshots__/generateNextSteps.test.ts.snap deleted file mode 100644 index df9a0ec6e..000000000 --- a/src/shared/__snapshots__/generateNextSteps.test.ts.snap +++ /dev/null @@ -1,183 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`generateNextSteps > {"excludeAllContributors":false,"excludeReleases":false,"excludeRenovate":false,"excludeTests":false} 1`] = ` -[ - { - "label": "Be sure to:", - "lines": [ - "- enable the GitHub apps: - - Codecov (https://github.com/apps/codecov) - - Renovate (https://github.com/apps/renovate)", - "- populate the secrets: - - ACCESS_TOKEN (a GitHub PAT with repo and workflow permissions) - - NPM_TOKEN (an npm access token with automation permissions)", - ], - }, -] -`; - -exports[`generateNextSteps > {"excludeAllContributors":false,"excludeReleases":false,"excludeRenovate":false,"excludeTests":true} 1`] = ` -[ - { - "label": "Be sure to:", - "lines": [ - "- enable the Renovate GitHub app (https://github.com/apps/renovate).", - "- populate the secrets: - - ACCESS_TOKEN (a GitHub PAT with repo and workflow permissions) - - NPM_TOKEN (an npm access token with automation permissions)", - ], - }, -] -`; - -exports[`generateNextSteps > {"excludeAllContributors":false,"excludeReleases":false,"excludeRenovate":true,"excludeTests":false} 1`] = ` -[ - { - "label": "Be sure to:", - "lines": [ - "- enable the Codecov GitHub app (https://github.com/apps/codecov).", - "- populate the secrets: - - ACCESS_TOKEN (a GitHub PAT with repo and workflow permissions) - - NPM_TOKEN (an npm access token with automation permissions)", - ], - }, -] -`; - -exports[`generateNextSteps > {"excludeAllContributors":false,"excludeReleases":false,"excludeRenovate":true,"excludeTests":true} 1`] = ` -[ - { - "label": "Be sure to populate the secrets: - - ACCESS_TOKEN (a GitHub PAT with repo and workflow permissions) - - NPM_TOKEN (an npm access token with automation permissions)", - }, -] -`; - -exports[`generateNextSteps > {"excludeAllContributors":false,"excludeReleases":true,"excludeRenovate":false,"excludeTests":false} 1`] = ` -[ - { - "label": "Be sure to:", - "lines": [ - "- enable the GitHub apps: - - Codecov (https://github.com/apps/codecov) - - Renovate (https://github.com/apps/renovate)", - "- populate the ACCESS_TOKEN secret (a GitHub PAT with repo and workflow permissions).", - ], - }, -] -`; - -exports[`generateNextSteps > {"excludeAllContributors":false,"excludeReleases":true,"excludeRenovate":false,"excludeTests":true} 1`] = ` -[ - { - "label": "Be sure to:", - "lines": [ - "- enable the Renovate GitHub app (https://github.com/apps/renovate).", - "- populate the ACCESS_TOKEN secret (a GitHub PAT with repo and workflow permissions).", - ], - }, -] -`; - -exports[`generateNextSteps > {"excludeAllContributors":false,"excludeReleases":true,"excludeRenovate":true,"excludeTests":false} 1`] = ` -[ - { - "label": "Be sure to:", - "lines": [ - "- enable the Codecov GitHub app (https://github.com/apps/codecov).", - "- populate the ACCESS_TOKEN secret (a GitHub PAT with repo and workflow permissions).", - ], - }, -] -`; - -exports[`generateNextSteps > {"excludeAllContributors":false,"excludeReleases":true,"excludeRenovate":true,"excludeTests":true} 1`] = ` -[ - { - "label": "Be sure to populate the ACCESS_TOKEN secret (a GitHub PAT with repo and workflow permissions).", - }, -] -`; - -exports[`generateNextSteps > {"excludeAllContributors":true,"excludeReleases":false,"excludeRenovate":false,"excludeTests":false} 1`] = ` -[ - { - "label": "Be sure to:", - "lines": [ - "- enable the GitHub apps: - - Codecov (https://github.com/apps/codecov) - - Renovate (https://github.com/apps/renovate)", - "- populate the secrets: - - ACCESS_TOKEN (a GitHub PAT with repo and workflow permissions) - - NPM_TOKEN (an npm access token with automation permissions)", - ], - }, -] -`; - -exports[`generateNextSteps > {"excludeAllContributors":true,"excludeReleases":false,"excludeRenovate":false,"excludeTests":true} 1`] = ` -[ - { - "label": "Be sure to:", - "lines": [ - "- enable the Renovate GitHub app (https://github.com/apps/renovate).", - "- populate the secrets: - - ACCESS_TOKEN (a GitHub PAT with repo and workflow permissions) - - NPM_TOKEN (an npm access token with automation permissions)", - ], - }, -] -`; - -exports[`generateNextSteps > {"excludeAllContributors":true,"excludeReleases":false,"excludeRenovate":true,"excludeTests":false} 1`] = ` -[ - { - "label": "Be sure to:", - "lines": [ - "- enable the Codecov GitHub app (https://github.com/apps/codecov).", - "- populate the secrets: - - ACCESS_TOKEN (a GitHub PAT with repo and workflow permissions) - - NPM_TOKEN (an npm access token with automation permissions)", - ], - }, -] -`; - -exports[`generateNextSteps > {"excludeAllContributors":true,"excludeReleases":false,"excludeRenovate":true,"excludeTests":true} 1`] = ` -[ - { - "label": "Be sure to populate the secrets: - - ACCESS_TOKEN (a GitHub PAT with repo and workflow permissions) - - NPM_TOKEN (an npm access token with automation permissions)", - }, -] -`; - -exports[`generateNextSteps > {"excludeAllContributors":true,"excludeReleases":true,"excludeRenovate":false,"excludeTests":false} 1`] = ` -[ - { - "label": "Be sure to enable the GitHub apps: - - Codecov (https://github.com/apps/codecov) - - Renovate (https://github.com/apps/renovate)", - }, -] -`; - -exports[`generateNextSteps > {"excludeAllContributors":true,"excludeReleases":true,"excludeRenovate":false,"excludeTests":true} 1`] = ` -[ - { - "label": "Be sure to enable the Renovate GitHub app (https://github.com/apps/renovate).", - }, -] -`; - -exports[`generateNextSteps > {"excludeAllContributors":true,"excludeReleases":true,"excludeRenovate":true,"excludeTests":false} 1`] = ` -[ - { - "label": "Be sure to enable the Codecov GitHub app (https://github.com/apps/codecov).", - }, -] -`; - -exports[`generateNextSteps > {"excludeAllContributors":true,"excludeReleases":true,"excludeRenovate":true,"excludeTests":true} 1`] = `[]`; diff --git a/src/shared/cli/lines.ts b/src/shared/cli/lines.ts deleted file mode 100644 index d1075ad4c..000000000 --- a/src/shared/cli/lines.ts +++ /dev/null @@ -1,14 +0,0 @@ -import chalk from "chalk"; - -export function logLine(line?: string) { - console.log(makeLine(line)); -} - -export function logNewSection(line: string) { - logLine(); - console.log(`◇ ${line}`); -} - -export function makeLine(line: string | undefined) { - return [chalk.gray("│"), line].filter(Boolean).join(" "); -} diff --git a/src/shared/cli/lowerFirst.ts b/src/shared/cli/lowerFirst.ts deleted file mode 100644 index 3b96e98f2..000000000 --- a/src/shared/cli/lowerFirst.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function lowerFirst(text: string) { - return text[0].toLowerCase() + text.slice(1); -} diff --git a/src/shared/cli/outro.test.ts b/src/shared/cli/outro.test.ts deleted file mode 100644 index 9f228a5ab..000000000 --- a/src/shared/cli/outro.test.ts +++ /dev/null @@ -1,61 +0,0 @@ -import chalk from "chalk"; -import { beforeEach, describe, expect, it, MockInstance, vi } from "vitest"; - -import { outro } from "./outro.js"; - -const mockOutro = vi.fn(); - -vi.mock("@clack/prompts", () => ({ - get outro() { - return mockOutro; - }, -})); - -let mockConsoleLog: MockInstance; - -describe("outro", () => { - beforeEach(() => { - mockConsoleLog = vi - .spyOn(console, "log") - .mockImplementation(() => undefined); - }); - - it("logs only basic statements when no lines are provided", () => { - outro([{ label: "Abc 123" }]); - - expect(mockConsoleLog.mock.calls).toEqual([ - [chalk.blue("Abc 123")], - [], - [chalk.greenBright(`See ya! 👋`)], - [], - ]); - }); - - it("also logs lines when provided", () => { - outro([{ label: "Abc 123", lines: ["one", "two"] }]); - - expect(mockConsoleLog.mock.calls).toEqual([ - [chalk.blue("Abc 123")], - [], - ["one"], - ["two"], - [], - [chalk.greenBright(`See ya! 👋`)], - [], - ]); - }); - - it("logs lines as code when variant is specified", () => { - outro([{ label: "Abc 123", lines: ["one", "two"], variant: "code" }]); - - expect(mockConsoleLog.mock.calls).toEqual([ - [chalk.blue("Abc 123")], - [], - [chalk.gray("one")], - [chalk.gray("two")], - [], - [chalk.greenBright(`See ya! 👋`)], - [], - ]); - }); -}); diff --git a/src/shared/cli/outro.ts b/src/shared/cli/outro.ts deleted file mode 100644 index 444891de1..000000000 --- a/src/shared/cli/outro.ts +++ /dev/null @@ -1,28 +0,0 @@ -import * as prompts from "@clack/prompts"; -import chalk from "chalk"; - -export interface OutroGroup { - label: string; - lines?: string[]; - variant?: "code"; -} - -export function outro(groups: OutroGroup[]) { - prompts.outro(chalk.blue(`Great, looks like the script finished! 🎉`)); - - for (const { label, lines, variant } of groups) { - console.log(chalk.blue(label)); - console.log(); - - if (lines) { - for (const line of lines) { - console.log(variant === "code" ? chalk.gray(line) : line); - } - - console.log(); - } - } - - console.log(chalk.greenBright(`See ya! 👋`)); - console.log(); -} diff --git a/src/shared/cli/spinners.ts b/src/shared/cli/spinners.ts deleted file mode 100644 index 00a271f86..000000000 --- a/src/shared/cli/spinners.ts +++ /dev/null @@ -1,72 +0,0 @@ -import * as prompts from "@clack/prompts"; -import chalk from "chalk"; -import readline from "readline"; - -import { logLine, logNewSection, makeLine } from "./lines.js"; -import { lowerFirst } from "./lowerFirst.js"; -import { startLineWithDots } from "./startLineWithDots.js"; - -const s = prompts.spinner(); - -export type LabeledSpinnerTask = [string, SpinnerTask]; - -export type SpinnerTask = () => Promise; - -export async function withSpinner( - label: string, - task: SpinnerTask, -) { - s.start(`${label}...`); - - try { - const result = await task(); - - s.stop(chalk.green(`✅ Passed ${lowerFirst(label)}.`)); - - return result; - } catch (error) { - s.stop(chalk.red(`❌ Error ${lowerFirst(label)}.`)); - - throw new Error(`Failed ${lowerFirst(label)}`, { cause: error }); - } -} - -export async function withSpinners( - label: string, - tasks: LabeledSpinnerTask[], -) { - logNewSection(`${label}...`); - - let currentLabel!: string; - let lastLogged!: string; - - for (const [label, run] of tasks) { - currentLabel = label; - - const line = makeLine(chalk.gray(` - ${label}`)); - const stopWriting = startLineWithDots(line); - - try { - await run(); - } catch (error) { - const descriptor = `${lowerFirst(label)} > ${lowerFirst(currentLabel)}`; - - logLine(chalk.red(`❌ Error ${descriptor}.`)); - - throw new Error(`Failed ${descriptor}`, { cause: error }); - } finally { - const lineLength = stopWriting(); - readline.clearLine(process.stdout, -1); - readline.moveCursor(process.stdout, -lineLength, 0); - } - - lastLogged = chalk.gray(`${line} ✔️\n`); - - process.stdout.write(lastLogged); - } - - readline.moveCursor(process.stdout, -lastLogged.length, -tasks.length - 2); - readline.clearScreenDown(process.stdout); - - logNewSection(chalk.green(`✅ Passed ${lowerFirst(label)}.`)); -} diff --git a/src/shared/cli/startLineWithDots.ts b/src/shared/cli/startLineWithDots.ts deleted file mode 100644 index e80378160..000000000 --- a/src/shared/cli/startLineWithDots.ts +++ /dev/null @@ -1,38 +0,0 @@ -import readline from "readline"; - -export function startLineWithDots(line: string) { - const timer = [setTimeout(tick, 500)]; - let dots = 0; - let lastLogged!: string; - - function clearLine() { - readline.clearLine(process.stdout, -1); - readline.moveCursor(process.stdout, -lastLogged.length, 0); - } - - function writeLine() { - dots = (dots + 1) % 4; - - const toLog = `${line}${".".repeat(dots)}`; - - process.stdout.write(toLog); - - lastLogged = toLog; - - return toLog; - } - - function tick() { - clearLine(); - writeLine(); - timer[0] = setTimeout(tick, 500); - } - - writeLine(); - - return () => { - clearLine(); - clearInterval(timer[0]); - return lastLogged.length; - }; -} diff --git a/src/shared/codes.ts b/src/shared/codes.ts deleted file mode 100644 index 89b39571e..000000000 --- a/src/shared/codes.ts +++ /dev/null @@ -1,7 +0,0 @@ -export const StatusCodes = { - Cancelled: 2, - Failure: 1, - Success: 0, -} as const; - -export type StatusCode = (typeof StatusCodes)[keyof typeof StatusCodes]; diff --git a/src/shared/createCleanupCommands.test.ts b/src/shared/createCleanupCommands.test.ts deleted file mode 100644 index 8c2117f7e..000000000 --- a/src/shared/createCleanupCommands.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { describe, expect, it } from "vitest"; - -import { createCleanupCommands } from "./createCleanupCommands.js"; - -describe("createCleanupCommands", () => { - it("only lints and formats when bin is not enabled and there are no prepended commands", () => { - const actual = createCleanupCommands(undefined); - - expect(actual).toEqual(["pnpm lint --fix", "pnpm format --write"]); - }); - - it("runs prepended commands before it lints and formats when bin is not enabled", () => { - const actual = createCleanupCommands(undefined, "prepended"); - - expect(actual).toEqual([ - "prepended", - "pnpm lint --fix", - "pnpm format --write", - ]); - }); - - it("runs prepended commands before it builds, lints, and formats when bin is not enabled", () => { - const actual = createCleanupCommands("bin/index.js", "prepended"); - - expect(actual).toEqual([ - "prepended", - "pnpm build", - "pnpm lint --fix", - "pnpm format --write", - ]); - }); -}); diff --git a/src/shared/createCleanupCommands.ts b/src/shared/createCleanupCommands.ts deleted file mode 100644 index d48c6477f..000000000 --- a/src/shared/createCleanupCommands.ts +++ /dev/null @@ -1,12 +0,0 @@ -export function createCleanupCommands( - bin: string | undefined, - ...prependedCommands: string[] -) { - return [ - ...prependedCommands, - // n/no-missing-import rightfully reports on a missing the bin .js file - ...(bin ? ["pnpm build"] : []), - "pnpm lint --fix", - "pnpm format --write", - ]; -} diff --git a/src/shared/doesRepositoryExist.test.ts b/src/shared/doesRepositoryExist.test.ts deleted file mode 100644 index 5932d647e..000000000 --- a/src/shared/doesRepositoryExist.test.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Octokit } from "octokit"; -import { describe, expect, it, MockInstance, vi } from "vitest"; - -import { doesRepositoryExist } from "./doesRepositoryExist.js"; - -const createMockOctokit = ( - repos: Partial>, -) => - ({ - rest: { - repos, - }, - }) as unknown as Octokit; - -const owner = "StubOwner"; -const repository = "stub-repository"; - -describe("doesRepositoryExist", () => { - it("returns true when the octokit GET resolves", async () => { - const octokit = createMockOctokit({ get: vi.fn().mockResolvedValue({}) }); - - const actual = await doesRepositoryExist(octokit, { owner, repository }); - - expect(actual).toBe(true); - }); - - it("returns false when the octokit GET rejects with a 404", async () => { - const octokit = createMockOctokit({ - get: vi.fn().mockRejectedValue({ status: 404 }), - }); - - const actual = await doesRepositoryExist(octokit, { owner, repository }); - - expect(actual).toBe(false); - }); - - it("throws the error when awaiting the octokit GET throws a non-404 error", async () => { - const error = new Error("Oh no!"); - const octokit = createMockOctokit({ - get: vi.fn().mockRejectedValue(error), - }); - - await expect( - async () => await doesRepositoryExist(octokit, { owner, repository }), - ).rejects.toEqual(error); - }); -}); diff --git a/src/shared/doesRepositoryExist.ts b/src/shared/doesRepositoryExist.ts deleted file mode 100644 index 8868a2f94..000000000 --- a/src/shared/doesRepositoryExist.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Octokit, RequestError } from "octokit"; - -export interface DoesRepositoryExistOptions { - owner: string; - repository: string; -} - -export async function doesRepositoryExist( - octokit: Octokit, - options: DoesRepositoryExistOptions, -) { - // Because the Octokit SDK throws on 404s (😡), - // we try/catch to check whether the repo exists. - try { - await octokit.rest.repos.get({ - owner: options.owner, - repo: options.repository, - }); - return true; - } catch (error) { - if ((error as RequestError).status !== 404) { - throw error; - } - - return false; - } -} diff --git a/src/shared/ensureGitRepository.test.ts b/src/shared/ensureGitRepository.test.ts deleted file mode 100644 index a9b4b7ec6..000000000 --- a/src/shared/ensureGitRepository.test.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; - -import { ensureGitRepository } from "./ensureGitRepository.js"; - -const mock$ = vi.fn(); - -vi.mock("execa", () => ({ - get $() { - return mock$; - }, -})); - -describe("ensureGitRepository", () => { - it("does not run git init when git status succeeds", async () => { - mock$.mockResolvedValue(0); - - await ensureGitRepository(); - - expect(mock$).toHaveBeenCalledTimes(1); - }); - - it("runs git init when git status fails", async () => { - mock$.mockRejectedValueOnce(1); - - await ensureGitRepository(); - - expect(mock$).toHaveBeenCalledWith(["git init"]); - }); -}); diff --git a/src/shared/ensureGitRepository.ts b/src/shared/ensureGitRepository.ts deleted file mode 100644 index 97ad7074d..000000000 --- a/src/shared/ensureGitRepository.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { $ } from "execa"; - -export async function ensureGitRepository() { - try { - await $`git status`; - } catch { - await $`git init`; - } -} diff --git a/src/shared/generateNextSteps.test.ts b/src/shared/generateNextSteps.test.ts deleted file mode 100644 index 88c83812a..000000000 --- a/src/shared/generateNextSteps.test.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { describe, expect, test } from "vitest"; - -import { generateNextSteps } from "./generateNextSteps.js"; -import { Options } from "./types.js"; - -const options = { - access: "public", - base: "everything", - description: "Test description.", - directory: ".", - email: { - github: "github@email.com", - npm: "npm@email.com", - }, - mode: "create", - owner: "TestOwner", - repository: "test-repository", - title: "Test Title", -} satisfies Options; - -describe("generateNextSteps", () => { - for (const excludeAllContributors of [false, true]) { - for (const excludeReleases of [false, true]) { - for (const excludeRenovate of [false, true]) { - for (const excludeTests of [false, true]) { - test( - // eslint-disable-next-line vitest/valid-title - JSON.stringify({ - excludeAllContributors, - excludeReleases, - excludeRenovate, - excludeTests, - }), - () => { - expect( - generateNextSteps({ - ...options, - excludeAllContributors, - excludeReleases, - excludeRenovate, - excludeTests, - }), - ).toMatchSnapshot(); - }, - ); - } - } - } - } -}); diff --git a/src/shared/generateNextSteps.ts b/src/shared/generateNextSteps.ts deleted file mode 100644 index c8eb8b998..000000000 --- a/src/shared/generateNextSteps.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { OutroGroup } from "./cli/outro.js"; -import { Options } from "./types.js"; - -export function generateNextSteps(options: Options): OutroGroup[] { - const lines = [ - joinedMessage( - "enable the", - [ - options.excludeTests || ["Codecov", "https://github.com/apps/codecov"], - options.excludeRenovate || [ - "Renovate", - "https://github.com/apps/renovate", - ], - ], - "GitHub app", - ), - joinedMessage( - "populate the", - [ - (options.excludeAllContributors && options.excludeReleases) || [ - "ACCESS_TOKEN", - "a GitHub PAT with repo and workflow permissions", - ], - options.excludeReleases || [ - "NPM_TOKEN", - "an npm access token with automation permissions", - ], - ], - "secret", - ), - ].filter((line): line is string => !!line); - - switch (lines.length) { - case 0: - return []; - case 1: - return [{ label: `Be sure to ${lines[0]}` }]; - default: - return [ - { - label: "Be sure to:", - lines: lines.map((line) => `- ${line}`), - }, - ]; - } -} - -const itemBreak = "\n - "; - -function joinedMessage( - prefix: string, - entries: ([string, string] | true)[], - suffix: string, -) { - const realEntries = entries.filter( - (entry): entry is [string, string] => entry !== true, - ); - - switch (realEntries.length) { - case 0: - return undefined; - case 1: - return `${prefix} ${realEntries[0][0]} ${suffix} (${realEntries[0][1]}).`; - default: - return `${prefix} ${suffix}s: ${itemBreak}${realEntries - .map((entry) => `${entry[0]} (${entry[1]})`) - .join(itemBreak)}`; - } -} diff --git a/src/shared/getGitHubUserAsAllContributor.test.ts b/src/shared/getGitHubUserAsAllContributor.test.ts deleted file mode 100644 index 25fe80e68..000000000 --- a/src/shared/getGitHubUserAsAllContributor.test.ts +++ /dev/null @@ -1,103 +0,0 @@ -import chalk from "chalk"; -import { Octokit } from "octokit"; -import { beforeEach, describe, expect, it, MockInstance, vi } from "vitest"; - -import { getGitHubUserAsAllContributor } from "./getGitHubUserAsAllContributor.js"; - -const mock$ = vi.fn(); - -vi.mock("execa", () => ({ - get $() { - return mock$; - }, -})); - -let mockConsoleWarn: MockInstance; - -const createMockOctokit = (getAuthenticated: MockInstance = vi.fn()) => - ({ - rest: { users: { getAuthenticated } }, - }) as unknown as Octokit; - -const owner = "TestOwner"; - -describe("getGitHubUserAsAllContributor", () => { - beforeEach(() => { - mockConsoleWarn = vi - .spyOn(console, "warn") - .mockImplementation(() => undefined); - }); - - it("defaults to owner with a log when options.offline is true", async () => { - const octokit = createMockOctokit(); - const actual = await getGitHubUserAsAllContributor(octokit, { - offline: true, - owner, - }); - - expect(actual).toEqual(owner); - expect(mockConsoleWarn).toHaveBeenCalledWith( - chalk.gray( - `Skipping populating all-contributors contributions for TestOwner because in --offline mode.`, - ), - ); - }); - - it("defaults to owner without a log when octokit is undefined", async () => { - const actual = await getGitHubUserAsAllContributor(undefined, { owner }); - - expect(actual).toEqual(owner); - expect(mockConsoleWarn).not.toHaveBeenCalled(); - }); - - it("uses the user from gh api user when it succeeds", async () => { - const login = "gh-api-user"; - const octokit = createMockOctokit( - vi.fn().mockResolvedValue({ data: { login } }), - ); - - await getGitHubUserAsAllContributor(octokit, { owner }); - - expect(mockConsoleWarn).not.toHaveBeenCalled(); - expect(mock$.mock.calls).toMatchInlineSnapshot(` - [ - [ - [ - "pnpx all-contributors-cli@6.25 add ", - " ", - "", - ], - "gh-api-user", - "code,content,doc,ideas,infra,maintenance,projectManagement,tool", - ], - ] - `); - }); - - it("defaults the user to the owner when gh api user fails", async () => { - const octokit = createMockOctokit( - vi.fn().mockRejectedValue(new Error("Oh no!")), - ); - - await getGitHubUserAsAllContributor(octokit, { owner }); - - expect(mockConsoleWarn).toHaveBeenCalledWith( - chalk.gray( - `Couldn't authenticate GitHub user, falling back to the provided owner name '${owner}'.`, - ), - ); - expect(mock$.mock.calls).toMatchInlineSnapshot(` - [ - [ - [ - "pnpx all-contributors-cli@6.25 add ", - " ", - "", - ], - "TestOwner", - "code,content,doc,ideas,infra,maintenance,projectManagement,tool", - ], - ] - `); - }); -}); diff --git a/src/shared/getGitHubUserAsAllContributor.ts b/src/shared/getGitHubUserAsAllContributor.ts deleted file mode 100644 index 95948e26f..000000000 --- a/src/shared/getGitHubUserAsAllContributor.ts +++ /dev/null @@ -1,50 +0,0 @@ -import chalk from "chalk"; -import { $ } from "execa"; -import { Octokit } from "octokit"; - -import { Options } from "./types.js"; - -export async function getGitHubUserAsAllContributor( - octokit: Octokit | undefined, - options: Pick, -) { - if (options.offline) { - console.warn( - chalk.gray( - `Skipping populating all-contributors contributions for ${options.owner} because in --offline mode.`, - ), - ); - return options.owner; - } - - let user: string; - - if (octokit) { - try { - user = (await octokit.rest.users.getAuthenticated()).data.login; - } catch { - console.warn( - chalk.gray( - `Couldn't authenticate GitHub user, falling back to the provided owner name '${options.owner}'.`, - ), - ); - user = options.owner; - } - } else { - user = options.owner; - } - - const contributions = [ - "code", - "content", - "doc", - "ideas", - "infra", - "maintenance", - "projectManagement", - "tool", - ].join(","); - await $`pnpx all-contributors-cli@6.25 add ${user} ${contributions}`; - - return user; -} diff --git a/src/shared/isUsingCreateEngine.ts b/src/shared/isUsingCreateEngine.ts deleted file mode 100644 index da251858c..000000000 --- a/src/shared/isUsingCreateEngine.ts +++ /dev/null @@ -1,5 +0,0 @@ -export function isUsingCreateEngine() { - return ( - !!process.env.CTA_CREATE_ENGINE && Boolean(process.env.CTA_CREATE_ENGINE) - ); -} diff --git a/src/shared/options/args.ts b/src/shared/options/args.ts deleted file mode 100644 index 670197edb..000000000 --- a/src/shared/options/args.ts +++ /dev/null @@ -1,332 +0,0 @@ -import chalk from "chalk"; - -export const allArgOptions = { - access: { - description: `(${chalk.cyanBright( - '"public" | "restricted"', - )}): Which ${chalk.cyanBright("npm publish --access")} to - release npm packages with (by default, "public")`, - docsSection: "optional", - type: "string", - }, - author: { - description: `Username on npm to publish packages under (by - default, an existing npm author, or the currently logged in npm user, or - ${chalk.cyanBright("owner.toLowerCase()")})`, - docsSection: "optional", - type: "string", - }, - auto: { - description: `Whether to infer all options from files on disk.`, - docsSection: "optional", - type: "boolean", - }, - base: { - description: `Whether to scaffold the repository with: - • everything: that comes with the template (${chalk.cyanBright.bold( - "recommended", - )}) - • common: additions to the minimal starters such as releases and tests - • minimal: amounts of tooling, essentially opting out of everything - • prompt: for which portions to exclude`, - docsSection: "core", - type: "string", - }, - bin: { - description: `package.json bin value to include for npx-style running.`, - docsSection: "optional", - type: "string", - }, - "create-repository": { - description: `Whether to create a corresponding repository on github.com - (if it doesn't yet exist)`, - docsSection: "core", - type: "boolean", - }, - description: { - description: `Sentence case description of the repository - (e.g. A quickstart-friendly TypeScript package with lots of great - repository tooling. ✨)`, - docsSection: "core", - type: "string", - }, - directory: { - description: `Directory to create the repository in (by default, the same - name as the repository)`, - docsSection: "optional", - type: "string", - }, - email: { - description: `Email address to be listed as the point of contact in docs - and packages (e.g. example@joshuakgoldberg.com)`, - docsSection: "optional", - type: "string", - }, - "email-github": { - description: `Optionally, may be provided to use different emails in ${chalk.cyanBright( - ".md", - )} - files`, - docsSection: "optional", - type: "string", - }, - "email-npm": { - description: `Optionally, may be provided to use different emails in - ${chalk.cyanBright("package.json")}`, - docsSection: "optional", - type: "string", - }, - "exclude-all-contributors": { - description: `Don't add all-contributors to track contributions - and display them in a README.md table.`, - docsSection: "opt-out", - type: "boolean", - }, - "exclude-compliance": { - description: `Don't add a GitHub Actions workflow to verify that PRs match - an expected format.`, - docsSection: "opt-out", - type: "boolean", - }, - "exclude-lint-jsdoc": { - description: `Don't use eslint-plugin-jsdoc to enforce good practices around - JSDoc comments.`, - docsSection: "opt-out", - type: "boolean", - }, - "exclude-lint-json": { - description: `Don't apply linting and sorting to ${chalk.cyanBright( - "*.json", - )}, and - ${chalk.cyanBright("*.jsonc")} files.`, - docsSection: "opt-out", - type: "boolean", - }, - "exclude-lint-knip": { - description: `Don't add Knip to detect unused files, dependencies, and code - exports.`, - docsSection: "opt-out", - type: "boolean", - }, - "exclude-lint-md": { - description: `Don't apply linting to ${chalk.cyanBright("*.md")} files.`, - docsSection: "opt-out", - type: "boolean", - }, - "exclude-lint-package-json": { - description: `Don't add eslint-plugin-package-json to lint for - package.json correctness.`, - docsSection: "opt-out", - type: "boolean", - }, - "exclude-lint-packages": { - description: `Don't add a pnpm dedupe workflow to ensure packages - aren't duplicated unnecessarily.`, - docsSection: "opt-out", - type: "boolean", - }, - "exclude-lint-perfectionist": { - description: `Don't apply eslint-plugin-perfectionist to ensure - imports, keys, and so on are in sorted order.`, - docsSection: "opt-out", - type: "boolean", - }, - "exclude-lint-regexp": { - description: `Don't add eslint-plugin-regexp to enforce good practices around - regular expressions.`, - docsSection: "opt-out", - type: "boolean", - }, - "exclude-lint-spelling": { - description: `Don't add cspell to spell check against dictionaries - of known words.`, - docsSection: "opt-out", - type: "boolean", - }, - "exclude-lint-strict": { - description: `Don't augment the recommended logical lint rules with - typescript-eslint's strict config.`, - docsSection: "opt-out", - type: "boolean", - }, - "exclude-lint-stylistic": { - description: `Don't add stylistic rules such as typescript-eslint's - stylistic config.`, - docsSection: "opt-out", - type: "boolean", - }, - "exclude-lint-yml": { - description: `Don't apply linting and sorting to ${chalk.cyanBright( - "*.yaml", - )} and ${chalk.cyanBright("*.yml")} files.`, - docsSection: "opt-out", - type: "boolean", - }, - "exclude-releases": { - description: `Don't add release-it to generate changelogs, package bumps, - and publishes based on conventional commits.`, - docsSection: "opt-out", - type: "boolean", - }, - "exclude-renovate": { - description: `Don't add a Renovate config to dependencies up-to-date with - PRs.`, - docsSection: "opt-out", - type: "boolean", - }, - "exclude-templated-by": { - description: `Don't add a _"This package was templated with create-typescript-app"_ notice at the end of the README.md.`, - docsSection: "opt-out", - type: "boolean", - }, - "exclude-tests": { - description: `Don't add Vitest tooling for fast unit tests, configured - with coverage tracking.`, - docsSection: "opt-out", - type: "boolean", - }, - funding: { - description: `GitHub organization or username to mention in ${chalk.cyanBright( - "funding.yml", - )} - (by default, owner)`, - docsSection: "optional", - type: "string", - }, - guide: { - description: `Link to a contribution guide to place at the top of the - development docs`, - docsSection: "optional", - type: "string", - }, - "guide-title": { - description: `If ${chalk.cyanBright( - "--guide", - )} is provided or detected from an existing - DEVELOPMENT.md, the text title to place in the guide link`, - docsSection: "optional", - type: "string", - }, - keywords: { - description: `Any number of keywords to include in ${chalk.cyanBright( - "package.json", - )} (by default, - none). This can be specified any number of times, like - ${chalk.cyanBright('--keywords apple --keywords "banana cherry"')}`, - docsSection: "optional", - multiple: true, - type: "string", - }, - logo: { - description: `Local image file in the repository to display near the top of - the README.md as a logo`, - docsSection: "optional", - type: "string", - }, - "logo-alt": { - description: `If ${chalk.cyanBright( - "--logo", - )} is provided or detected from an existing README.md, - alt text that describes the image will be prompted for if not provided`, - docsSection: "optional", - type: "string", - }, - "logo-height": { - description: `If ${chalk.cyanBright( - "--logo", - )} is provided or detected from an existing README.md, - an explicit height style`, - docsSection: "optional", - type: "string", - }, - "logo-width": { - description: `If ${chalk.cyanBright( - "--logo", - )} is provided or detected from an existing README.md, - an explicit width style`, - docsSection: "optional", - type: "string", - }, - mode: { - description: `Whether to: - • create: a new repository in a child directory - • initialize: a freshly repository in the current directory - • migrate: an existing repository in the current directory`, - docsSection: "core", - type: "string", - }, - offline: { - description: `You can run ${chalk.cyanBright( - "create-typescript-app", - )} in an "offline" mode. - Doing so will: - • Enable ${chalk.cyanBright( - "--exclude-all-contributors-api", - )} and ${chalk.cyanBright("--skip-github-api")} - • Skip network calls when setting up contributors - • Run pnpm commands with pnpm's ${chalk.cyanBright("--offline")} mode`, - docsSection: "offline", - type: "boolean", - }, - owner: { - description: `GitHub organization or user the repository is underneath - (e.g. JoshuaKGoldberg)`, - docsSection: "core", - type: "string", - }, - "preserve-generated-from": { - description: `Whether to keep the GitHub repository generated from - notice (by default, false)`, - docsSection: "optional", - type: "boolean", - }, - repository: { - description: `The kebab-case name of the repository - (e.g. create-typescript-app)`, - docsSection: "core", - type: "string", - }, - "skip-all-contributors-api": { - description: `Skips network calls that fetch all-contributors data from - GitHub. This flag does nothing if ${chalk.cyanBright( - "--exclude-all-contributors", - )} was specified.`, - docsSection: "skip-net", - type: "boolean", - }, - "skip-github-api": { - description: `Skips calling to GitHub APIs.`, - docsSection: "skip-net", - type: "boolean", - }, - "skip-install": { - description: `Skips installing all the new template packages with pnpm.`, - docsSection: "skip-net", - type: "boolean", - }, - "skip-removal": { - description: `Skips removing setup docs and scripts, including this ${chalk.cyanBright( - "docs/", - )} - directory`, - docsSection: "skip-disk", - type: "boolean", - }, - "skip-restore": { - description: `Skips the prompt offering to restore the repository if an - error occurs during setup`, - docsSection: "skip-disk", - type: "boolean", - }, - "skip-uninstall": { - description: `Skips uninstalling packages only used for setup scripts`, - docsSection: "skip-disk", - type: "boolean", - }, - title: { - description: `Title Case title for the repository to be used in - documentation (e.g. Create TypeScript App)`, - docsSection: "core", - type: "string", - }, -} as const; diff --git a/src/shared/options/augmentOptionsWithExcludes.test.ts b/src/shared/options/augmentOptionsWithExcludes.test.ts deleted file mode 100644 index ed95d1354..000000000 --- a/src/shared/options/augmentOptionsWithExcludes.test.ts +++ /dev/null @@ -1,189 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; - -import { Options } from "../types.js"; -import { augmentOptionsWithExcludes } from "./augmentOptionsWithExcludes.js"; - -const mockMultiselect = vi.fn(); -const mockSelect = vi.fn(); - -vi.mock("@clack/prompts", () => ({ - isCancel: () => false, - get multiselect() { - return mockMultiselect; - }, - get select() { - return mockSelect; - }, -})); - -const optionsBase = { - access: "public", - author: undefined, - base: undefined, - description: "", - directory: ".", - email: { - github: "github@email.com", - npm: "npm@email.com", - }, - excludeAllContributors: undefined, - excludeBuild: undefined, - excludeCompliance: undefined, - excludeLintESLint: undefined, - excludeLintJSDoc: undefined, - excludeLintJson: undefined, - excludeLintKnip: undefined, - excludeLintMd: undefined, - excludeLintPackageJson: undefined, - excludeLintPackages: undefined, - excludeLintPerfectionist: undefined, - excludeLintRegexp: undefined, - excludeLintSpelling: undefined, - excludeLintStrict: undefined, - excludeLintStylistic: undefined, - excludeLintYml: undefined, - excludeReleases: undefined, - excludeRenovate: undefined, - excludeTemplatedBy: undefined, - excludeTests: undefined, - funding: undefined, - logo: undefined, - mode: "create", - offline: true, - owner: "", - repository: "", - skipGitHubApi: false, - skipInstall: undefined, - skipRemoval: undefined, - skipRestore: undefined, - skipUninstall: undefined, - title: "", -} satisfies Options; - -describe("augmentOptionsWithExcludes", () => { - it("returns options directly when no exclusions are provided and 'base' is provided for the prompt", async () => { - const base = "everything"; - - mockSelect.mockResolvedValueOnce(base); - - const actual = await augmentOptionsWithExcludes(optionsBase); - - expect(actual).toEqual({ - ...optionsBase, - base, - }); - }); - - it("returns options based on the select when no exclusions are provided and 'prompt' is provided for the prompt", async () => { - const base = "prompt"; - - mockSelect.mockResolvedValueOnce(base); - mockMultiselect.mockResolvedValue([]); - - const actual = await augmentOptionsWithExcludes(optionsBase); - - expect(actual).toEqual({ - ...optionsBase, - base, - excludeAllContributors: true, - excludeBuild: true, - excludeCompliance: true, - excludeLintESLint: true, - excludeLintJSDoc: true, - excludeLintJson: true, - excludeLintKnip: true, - excludeLintMd: true, - excludeLintPackageJson: true, - excludeLintPackages: true, - excludeLintPerfectionist: true, - excludeLintRegexp: true, - excludeLintSpelling: true, - excludeLintStrict: true, - excludeLintStylistic: true, - excludeLintYml: true, - excludeReleases: true, - excludeRenovate: true, - excludeTemplatedBy: true, - excludeTests: true, - }); - }); - - it("skips prompting returns options directly when exclusions are provided manually", async () => { - const options = { - ...optionsBase, - excludeCompliance: true, - } satisfies Options; - - const actual = await augmentOptionsWithExcludes(options); - - expect(actual).toBe(options); - }); - - it("uses the 'common' base without prompting when provided manually", async () => { - const options = { - ...optionsBase, - base: "common", - } satisfies Options; - - const actual = await augmentOptionsWithExcludes(options); - - expect(actual).toEqual({ - ...options, - excludeCompliance: true, - excludeLintESLint: true, - excludeLintJSDoc: true, - excludeLintJson: true, - excludeLintMd: true, - excludeLintPackageJson: true, - excludeLintPackages: true, - excludeLintPerfectionist: true, - excludeLintRegexp: true, - excludeLintSpelling: true, - excludeLintStrict: true, - excludeLintStylistic: true, - excludeLintYml: true, - }); - }); - - it("uses the 'minimal' base without prompting when provided manually", async () => { - const options = { - ...optionsBase, - base: "minimal", - } satisfies Options; - - const actual = await augmentOptionsWithExcludes(options); - - expect(actual).toEqual({ - ...options, - excludeAllContributors: true, - excludeCompliance: true, - excludeLintESLint: true, - excludeLintJSDoc: true, - excludeLintJson: true, - excludeLintKnip: true, - excludeLintMd: true, - excludeLintPackageJson: true, - excludeLintPackages: true, - excludeLintPerfectionist: true, - excludeLintRegexp: true, - excludeLintSpelling: true, - excludeLintStrict: true, - excludeLintStylistic: true, - excludeLintYml: true, - excludeReleases: true, - excludeRenovate: true, - excludeTests: true, - }); - }); - - it("uses the 'everything' base without prompting when provided manually", async () => { - const options = { - ...optionsBase, - base: "everything", - } satisfies Options; - - const actual = await augmentOptionsWithExcludes(options); - - expect(actual).toStrictEqual(options); - }); -}); diff --git a/src/shared/options/augmentOptionsWithExcludes.ts b/src/shared/options/augmentOptionsWithExcludes.ts deleted file mode 100644 index 47070d669..000000000 --- a/src/shared/options/augmentOptionsWithExcludes.ts +++ /dev/null @@ -1,108 +0,0 @@ -import * as prompts from "@clack/prompts"; -import chalk from "chalk"; - -import { filterPromptCancel } from "../prompts.js"; -import { Options, OptionsBase } from "../types.js"; -import { - exclusionDescriptions, - ExclusionKey, - exclusionKeys, - getExclusions, -} from "./exclusionKeys.js"; - -export async function augmentOptionsWithExcludes( - options: Options, -): Promise { - if ( - Object.keys(options).some( - (key) => - key in exclusionDescriptions && - options[key as keyof typeof options] !== undefined, - ) - ) { - return options; - } - - const base = - options.base ?? - filterPromptCancel( - await prompts.select({ - initialValue: "common" as OptionsBase, - message: `How much tooling would you like the template to set up for you?`, - options: [ - { - label: makeLabel( - "everything", - "The most comprehensive tooling imaginable: sorting, spellchecking, and more!", - ), - value: "everything", - }, - { - hint: "recommended", - label: makeLabel( - "common", - "Bare starters plus testing and automation for all-contributors and releases.", - ), - value: "common", - }, - { - label: makeLabel( - "minimal", - "Just bare starter tooling: building, formatting, linting, and type checking.", - ), - value: "minimal", - }, - { - label: makeLabel("prompt", "(allow me to customize)"), - value: "prompt", - }, - ], - }), - ); - - switch (base) { - case "common": - case "everything": - case "minimal": - return { - ...options, - base, - ...getExclusions(options, base), - }; - case "prompt": { - const exclusionsNotEnabled = new Set( - filterPromptCancel( - await prompts.multiselect({ - initialValues: exclusionKeys, - message: - "Select the tooling portions you'd like to remove. All are enabled by default. Press ↑ or ↓ to change the selected item, then space to select.", - options: Object.entries(exclusionDescriptions).map( - ([value, { hint, label }]) => ({ - hint, - label, - value: value as ExclusionKey, - }), - ), - }), - ), - ); - - return { - ...options, - base, - ...Object.fromEntries( - exclusionKeys.map( - (exclusionKey) => - [exclusionKey, !exclusionsNotEnabled.has(exclusionKey)] as const, - ), - ), - }; - } - case undefined: - return undefined; - } -} - -function makeLabel(label: string, message: string) { - return [chalk.bold(label), message].join("\t "); -} diff --git a/src/shared/options/createOptionDefaults/index.test.ts b/src/shared/options/createOptionDefaults/index.test.ts deleted file mode 100644 index 54189becd..000000000 --- a/src/shared/options/createOptionDefaults/index.test.ts +++ /dev/null @@ -1,218 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; - -import { createOptionDefaults } from "./index.js"; - -const mock$ = vi.fn(); - -vi.mock("execa", () => ({ - get $() { - return mock$; - }, -})); - -const mockGitUrlParse = vi.fn(); - -vi.mock("git-url-parse", () => ({ - get default() { - return mockGitUrlParse; - }, -})); - -const mockNpmUser = vi.fn(); - -vi.mock("npm-user", () => ({ - get default() { - return mockNpmUser; - }, -})); - -const mockReadPackageData = vi.fn(); - -vi.mock("../../packages.js", () => ({ - get readPackageData() { - return mockReadPackageData; - }, -})); - -const mockReadGitHubEmail = vi.fn(); - -vi.mock("./readGitHubEmail.js", () => ({ - get readGitHubEmail() { - return mockReadGitHubEmail; - }, -})); - -describe("createOptionDefaults", () => { - describe("bin", () => { - it("returns undefined when package data does not have a bin", async () => { - mockReadPackageData.mockResolvedValue({}); - - const actual = await createOptionDefaults().bin(); - - expect(actual).toBeUndefined(); - }); - - it("returns the bin when package data has a bin", async () => { - const bin = "./lib/index.js"; - - mockReadPackageData.mockResolvedValue({ bin }); - - const actual = await createOptionDefaults().bin(); - - expect(actual).toBe(bin); - }); - }); - - describe("email", () => { - beforeEach(() => { - mockNpmUser.mockImplementation((username: string) => ({ - email: `npm-${username}@test.com`, - })); - }); - - it("returns the npm whoami email from npm when only an npm exists", async () => { - mock$.mockImplementation(([command]: string[]) => - command === "npm whoami" ? { stdout: "username" } : undefined, - ); - mockReadGitHubEmail.mockResolvedValueOnce(undefined); - - const actual = await createOptionDefaults().email(); - - expect(actual).toEqual({ - github: "npm-username@test.com", - npm: "npm-username@test.com", - }); - }); - - it("returns the npm whoami email from npm when only a package author email exists", async () => { - mock$.mockResolvedValue({ stdout: "" }); - mockReadGitHubEmail.mockResolvedValueOnce(undefined); - mockReadPackageData.mockResolvedValue({ - author: { - email: "test@package.com", - }, - }); - - const actual = await createOptionDefaults().email(); - - expect(actual).toEqual({ - github: "test@package.com", - npm: "test@package.com", - }); - }); - - it("returns the github email when only a github email exists", async () => { - mock$.mockResolvedValue({ stdout: "" }); - mockReadPackageData.mockResolvedValueOnce({}); - mockReadGitHubEmail.mockResolvedValueOnce("github@test.com"); - - const actual = await createOptionDefaults().email(); - - expect(actual).toEqual({ - github: "github@test.com", - npm: "github@test.com", - }); - }); - - it("returns the git user email when only a git user email exists", async () => { - mock$.mockImplementation(([command]: string[]) => - command === "git config --get user.email" - ? { stdout: "git@test.com" } - : undefined, - ); - mockReadGitHubEmail.mockResolvedValueOnce(undefined); - mockReadPackageData.mockResolvedValue({}); - - const actual = await createOptionDefaults().email(); - - expect(actual).toEqual({ - github: "git@test.com", - npm: "git@test.com", - }); - }); - - it("returns both the git user email and the npm user email when only those two exist", async () => { - mock$.mockImplementation(([command]: string[]) => ({ - stdout: - command === "git config --get user.email" - ? "git@test.com" - : "username", - })); - mockReadGitHubEmail.mockResolvedValueOnce(undefined); - mockReadPackageData.mockResolvedValue({}); - - const actual = await createOptionDefaults().email(); - - expect(actual).toEqual({ - github: "git@test.com", - npm: "npm-username@test.com", - }); - }); - - it("returns all three emails when they all exist", async () => { - mock$.mockImplementation(([command]: string[]) => ({ - stdout: - command === "git config --get user.email" - ? "git@test.com" - : "username", - })); - mockReadGitHubEmail.mockResolvedValueOnce("github@test.com"); - mockReadPackageData.mockResolvedValue({}); - - const actual = await createOptionDefaults().email(); - - expect(actual).toEqual({ - github: "github@test.com", - npm: "npm-username@test.com", - }); - }); - - it("returns undefined when none of the emails exist", async () => { - mock$.mockResolvedValue({ stdout: "" }); - mockReadGitHubEmail.mockResolvedValueOnce(undefined); - mockReadPackageData.mockResolvedValue({}); - - const actual = await createOptionDefaults().email(); - - expect(actual).toBeUndefined(); - }); - }); - - describe("repository", () => { - it("returns promptedOptions.repository when it exists", async () => { - const repository = "test-prompted-repository"; - const promptedOptions = { repository }; - const actual = await createOptionDefaults(promptedOptions).repository(); - - expect(actual).toBe(repository); - }); - - it("returns the Git name when it exists and promptedOptions.repository doesn't", async () => { - const name = "test-git-repository"; - mockGitUrlParse.mockResolvedValueOnce({ name }); - - const actual = await createOptionDefaults().repository(); - - expect(actual).toBe(name); - }); - - it("returns the package name when it exists and promptedOptions.repository and Git name don't", async () => { - const name = "test-package-name"; - mockReadPackageData.mockResolvedValueOnce({ name }); - - const actual = await createOptionDefaults().repository(); - - expect(actual).toBe(name); - }); - - it("returns the directory when it exists and promptedOptions.repository, Git name, and package name don't", async () => { - mockReadPackageData.mockResolvedValueOnce({}); - const directory = "test-prompted-directory"; - const promptedOptions = { directory }; - - const actual = await createOptionDefaults(promptedOptions).repository(); - - expect(actual).toBe(directory); - }); - }); -}); diff --git a/src/shared/options/createOptionDefaults/index.ts b/src/shared/options/createOptionDefaults/index.ts deleted file mode 100644 index 3049463c8..000000000 --- a/src/shared/options/createOptionDefaults/index.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { $ } from "execa"; -import gitRemoteOriginUrl from "git-remote-origin-url"; -import gitUrlParse from "git-url-parse"; -import lazyValue from "lazy-value"; -import * as fs from "node:fs/promises"; -import npmUser from "npm-user"; - -import { readDescription } from "../../../next/readDescription.js"; -import { readPackageData } from "../../packages.js"; -import { readFileSafe } from "../../readFileSafe.js"; -import { tryCatchAsync } from "../../tryCatchAsync.js"; -import { tryCatchLazyValueAsync } from "../../tryCatchLazyValueAsync.js"; -import { PromptedOptions } from "../../types.js"; -import { parsePackageAuthor } from "./parsePackageAuthor.js"; -import { readDefaultsFromReadme } from "./readDefaultsFromReadme.js"; -import { readEmails } from "./readEmails.js"; -import { readGuide } from "./readGuide.js"; - -export function createOptionDefaults(promptedOptions?: PromptedOptions) { - const gitDefaults = tryCatchLazyValueAsync(async () => - gitUrlParse(await gitRemoteOriginUrl()), - ); - - const npmDefaults = tryCatchLazyValueAsync(async () => { - const whoami = (await $`npm whoami`).stdout; - return whoami ? await npmUser(whoami) : undefined; - }); - - const packageData = lazyValue(readPackageData); - const packageAuthor = lazyValue(async () => - parsePackageAuthor(await packageData()), - ); - - const readme = lazyValue(async () => await readFileSafe("README.md", "")); - - return { - author: async () => - (await packageAuthor()).author ?? (await npmDefaults())?.name, - bin: async () => (await packageData()).bin, - description: async () => await readDescription(packageData, readme), - email: async () => readEmails(npmDefaults, packageAuthor), - funding: async () => - await tryCatchAsync(async () => - (await fs.readFile(".github/FUNDING.yml")) - .toString() - .split(":")[1] - ?.trim(), - ), - guide: readGuide, - owner: async () => - (await gitDefaults())?.organization ?? (await packageAuthor()).author, - repository: async () => - promptedOptions?.repository ?? - (await gitDefaults())?.name ?? - (await packageData()).name ?? - promptedOptions?.directory, - ...readDefaultsFromReadme(readme, () => - Promise.resolve(promptedOptions?.repository), - ), - }; -} diff --git a/src/shared/options/createRepositoryWithApi.test.ts b/src/shared/options/createRepositoryWithApi.test.ts deleted file mode 100644 index 5f4ca68e9..000000000 --- a/src/shared/options/createRepositoryWithApi.test.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { Octokit } from "octokit"; -import { describe, expect, it, vi } from "vitest"; - -import { createRepositoryWithApi } from "./createRepositoryWithApi.js"; - -const options = { owner: "StubOwner", repository: "stub-repository" }; - -const mockCreateUsingTemplate = vi.fn(); -const mockCreateInOrg = vi.fn(); -const mockCreateForAuthenticatedUser = vi.fn(); -const mockGetAuthenticated = vi.fn(); - -const createMockOctokit = () => - ({ - rest: { - repos: { - createForAuthenticatedUser: mockCreateForAuthenticatedUser, - createInOrg: mockCreateInOrg, - createUsingTemplate: mockCreateUsingTemplate, - }, - users: { - getAuthenticated: mockGetAuthenticated, - }, - }, - }) as unknown as Octokit; - -describe("createRepositoryWithApi", () => { - it("creates using a template when preserveGeneratedFrom is true", async () => { - await createRepositoryWithApi(createMockOctokit(), { - ...options, - preserveGeneratedFrom: true, - }); - - expect(mockCreateForAuthenticatedUser).not.toHaveBeenCalled(); - expect(mockCreateInOrg).not.toHaveBeenCalled(); - expect(mockCreateUsingTemplate).toHaveBeenCalledWith({ - name: options.repository, - owner: options.owner, - template_owner: "JoshuaKGoldberg", - template_repo: "create-typescript-app", - }); - }); - - it("creates under the user when the user is the owner", async () => { - mockGetAuthenticated.mockResolvedValueOnce({ - data: { - login: options.owner, - }, - }); - await createRepositoryWithApi(createMockOctokit(), options); - - expect(mockCreateForAuthenticatedUser).toHaveBeenCalledWith({ - name: options.repository, - }); - expect(mockCreateInOrg).not.toHaveBeenCalled(); - expect(mockCreateUsingTemplate).not.toHaveBeenCalled(); - }); - - it("creates under an org when the user is not the owner", async () => { - const login = "other-user"; - mockGetAuthenticated.mockResolvedValueOnce({ data: { login } }); - await createRepositoryWithApi(createMockOctokit(), options); - - expect(mockCreateForAuthenticatedUser).not.toHaveBeenCalled(); - expect(mockCreateInOrg).toHaveBeenCalledWith({ - name: options.repository, - org: options.owner, - }); - expect(mockCreateUsingTemplate).not.toHaveBeenCalled(); - }); -}); diff --git a/src/shared/options/createRepositoryWithApi.ts b/src/shared/options/createRepositoryWithApi.ts deleted file mode 100644 index f8be9b3fd..000000000 --- a/src/shared/options/createRepositoryWithApi.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Octokit } from "octokit"; - -export interface CreateRepositoryWithApiOptions { - owner: string; - preserveGeneratedFrom?: boolean; - repository: string; -} - -export async function createRepositoryWithApi( - octokit: Octokit, - options: CreateRepositoryWithApiOptions, -) { - if (options.preserveGeneratedFrom) { - await octokit.rest.repos.createUsingTemplate({ - name: options.repository, - owner: options.owner, - template_owner: "JoshuaKGoldberg", - template_repo: "create-typescript-app", - }); - return; - } - - const currentUser = await octokit.rest.users.getAuthenticated(); - - try { - if (currentUser.data.login === options.owner) { - await octokit.rest.repos.createForAuthenticatedUser({ - name: options.repository, - }); - } else { - await octokit.rest.repos.createInOrg({ - name: options.repository, - org: options.owner, - }); - } - } catch (error) { - throw new Error("Failed to create new repository on GitHub.", { - cause: error, - }); - } -} diff --git a/src/shared/options/detectEmailRedundancy.test.ts b/src/shared/options/detectEmailRedundancy.test.ts deleted file mode 100644 index 3091a6096..000000000 --- a/src/shared/options/detectEmailRedundancy.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { describe, expect, it } from "vitest"; - -import { detectEmailRedundancy } from "./detectEmailRedundancy.js"; - -describe("detectEmailRedundancy", () => { - it("returns undefined when only email is specified", () => { - expect(detectEmailRedundancy({ email: "test@email.com" })).toBeUndefined(); - }); - - it("returns undefined when email-github and email-npm are specified while email is not", () => { - expect( - detectEmailRedundancy({ - "email-github": "test@email.com", - "email-npm": "test@email.com", - }), - ).toBeUndefined(); - }); - - it("returns a complaint when email-github is specified while email and email-npm are not", () => { - expect( - detectEmailRedundancy({ - "email-github": "test@email.com", - }), - ).toBe( - "If --email-github is specified, either --email or --email-npm should be.", - ); - }); - - it("returns a complaint when email-npm is specified while email and email-github are not", () => { - expect( - detectEmailRedundancy({ - "email-npm": "test@email.com", - }), - ).toBe( - "If --email-npm is specified, either --email or --email-github should be.", - ); - }); -}); diff --git a/src/shared/options/detectEmailRedundancy.ts b/src/shared/options/detectEmailRedundancy.ts deleted file mode 100644 index 15044b475..000000000 --- a/src/shared/options/detectEmailRedundancy.ts +++ /dev/null @@ -1,23 +0,0 @@ -export interface EmailValues { - email?: boolean | string; - "email-github"?: boolean | string; - "email-npm"?: boolean | string; -} - -export function detectEmailRedundancy(values: EmailValues) { - if (values.email) { - return values["email-github"] && values["email-npm"] - ? "--email should not be specified if both --email-github and --email-npm are specified." - : undefined; - } - - if (values["email-github"] && !values["email-npm"]) { - return "If --email-github is specified, either --email or --email-npm should be."; - } - - if (values["email-npm"] && !values["email-github"]) { - return "If --email-npm is specified, either --email or --email-github should be."; - } - - return undefined; -} diff --git a/src/shared/options/ensureRepositoryExists.test.ts b/src/shared/options/ensureRepositoryExists.test.ts deleted file mode 100644 index 3345f3adf..000000000 --- a/src/shared/options/ensureRepositoryExists.test.ts +++ /dev/null @@ -1,170 +0,0 @@ -import { Octokit } from "octokit"; -import { describe, expect, it, vi } from "vitest"; - -import { ensureRepositoryExists } from "./ensureRepositoryExists.js"; - -const mockSelect = vi.fn(); -const mockText = vi.fn(); - -vi.mock("@clack/prompts", () => ({ - intro: vi.fn(), - isCancel: vi.fn(), - outro: vi.fn(), - get select() { - return mockSelect; - }, - get text() { - return mockText; - }, -})); - -const mockDoesRepositoryExist = vi.fn(); - -vi.mock("../doesRepositoryExist.js", () => ({ - get doesRepositoryExist() { - return mockDoesRepositoryExist; - }, -})); - -const owner = "StubOwner"; -const repository = "stub-repository"; - -const mockCreateRepositoryWithApi = vi.fn(); - -vi.mock("./createRepositoryWithApi.js", () => ({ - get createRepositoryWithApi() { - return mockCreateRepositoryWithApi; - }, -})); - -const createMockOctokit = () => ({}) as unknown as Octokit; - -describe("ensureRepositoryExists", () => { - it("returns the repository when octokit is undefined", async () => { - const actual = await ensureRepositoryExists(undefined, { - mode: "initialize", - owner, - repository, - }); - - expect(actual).toEqual({ octokit: undefined, repository }); - }); - - it("returns the repository when octokit is defined and the repository exists", async () => { - mockDoesRepositoryExist.mockResolvedValue(true); - const octokit = createMockOctokit(); - const actual = await ensureRepositoryExists(octokit, { - mode: "initialize", - owner, - repository, - }); - - expect(actual).toEqual({ octokit, repository }); - }); - - it("creates a new repository when the prompt is 'create' and the repository does not exist", async () => { - const octokit = createMockOctokit(); - - mockDoesRepositoryExist.mockResolvedValue(false); - mockSelect.mockResolvedValue("create"); - - const actual = await ensureRepositoryExists(octokit, { - mode: "initialize", - owner, - repository, - }); - - expect(actual).toEqual({ octokit, repository }); - expect(mockCreateRepositoryWithApi).toHaveBeenCalledWith(octokit, { - owner, - preserveGeneratedFrom: undefined, - repository, - }); - }); - - it("defaults to creating a repository when mode is 'create'", async () => { - const octokit = createMockOctokit(); - - mockDoesRepositoryExist.mockResolvedValue(false); - - const actual = await ensureRepositoryExists(octokit, { - mode: "create", - owner, - repository, - }); - - expect(actual).toEqual({ octokit, repository }); - expect(mockCreateRepositoryWithApi).toHaveBeenCalledWith(octokit, { - owner, - preserveGeneratedFrom: undefined, - repository, - }); - expect(mockSelect).not.toHaveBeenCalled(); - }); - - it("returns the second repository when the prompt is 'different', the first repository does not exist, and the second repository exists", async () => { - const octokit = createMockOctokit(); - const newRepository = "new-repository"; - - mockDoesRepositoryExist - .mockResolvedValueOnce(false) - .mockResolvedValueOnce(true); - mockSelect.mockResolvedValueOnce("different"); - mockText.mockResolvedValue(newRepository); - - const actual = await ensureRepositoryExists(octokit, { - mode: "initialize", - owner, - repository, - }); - - expect(actual).toEqual({ - octokit, - repository: newRepository, - }); - expect(mockCreateRepositoryWithApi).not.toHaveBeenCalled(); - }); - - it("creates the second repository when the prompt is 'different', the first repository does not exist, and the second repository does not exist", async () => { - const octokit = createMockOctokit(); - const newRepository = "new-repository"; - - mockDoesRepositoryExist.mockResolvedValue(false); - mockSelect - .mockResolvedValueOnce("different") - .mockResolvedValueOnce("create"); - mockText.mockResolvedValue(newRepository); - - const actual = await ensureRepositoryExists(octokit, { - mode: "initialize", - owner, - repository, - }); - - expect(actual).toEqual({ - octokit, - repository: newRepository, - }); - expect(mockCreateRepositoryWithApi).toHaveBeenCalledWith(octokit, { - owner, - preserveGeneratedFrom: undefined, - repository: newRepository, - }); - }); - - it("switches octokit to undefined when the prompt is 'local' and the repository does not exist", async () => { - const octokit = createMockOctokit(); - - mockDoesRepositoryExist.mockResolvedValue(false); - mockSelect.mockResolvedValue("local"); - - const actual = await ensureRepositoryExists(octokit, { - mode: "initialize", - owner, - repository, - }); - - expect(actual).toEqual({ octokit: undefined, repository }); - expect(mockCreateRepositoryWithApi).not.toHaveBeenCalled(); - }); -}); diff --git a/src/shared/options/ensureRepositoryExists.ts b/src/shared/options/ensureRepositoryExists.ts deleted file mode 100644 index a04c9b707..000000000 --- a/src/shared/options/ensureRepositoryExists.ts +++ /dev/null @@ -1,95 +0,0 @@ -import * as prompts from "@clack/prompts"; -import { Octokit } from "octokit"; - -import { doesRepositoryExist } from "../doesRepositoryExist.js"; -import { isUsingCreateEngine } from "../isUsingCreateEngine.js"; -import { filterPromptCancel } from "../prompts.js"; -import { Options } from "../types.js"; -import { createRepositoryWithApi } from "./createRepositoryWithApi.js"; - -export type EnsureRepositoryExistsOptions = Pick< - Options, - "mode" | "owner" | "preserveGeneratedFrom" | "repository" ->; - -export interface RepositoryExistsResult { - octokit: Octokit | undefined; - repository: string; -} - -export async function ensureRepositoryExists( - octokit: Octokit | undefined, - options: EnsureRepositoryExistsOptions, -): Promise> { - // We'll only respect input options once before prompting for them - let { repository } = options; - let createRepository = options.mode === "create"; - - // We'll continuously pester the user for a repository - // until they bail, create a new one, or it exists. - while (octokit) { - if ( - isUsingCreateEngine() || - (await doesRepositoryExist(octokit, options)) - ) { - return { octokit, repository }; - } - - const selection = createRepository - ? "create" - : filterPromptCancel( - (await prompts.select({ - message: `Repository ${options.repository} doesn't seem to exist under ${options.owner}. What would you like to do?`, - options: [ - { label: "Create a new repository", value: "create" }, - { - label: "Switch to a different repository name", - value: "different", - }, - { - label: "Keep changes local", - value: "local", - }, - { label: "Bail out and maybe try again later", value: "bail" }, - ], - })) as "bail" | "create" | "different" | "local", - ); - - createRepository = false; - - switch (selection) { - case "bail": - case undefined: - return {}; - - case "create": - await createRepositoryWithApi(octokit, { - owner: options.owner, - preserveGeneratedFrom: options.preserveGeneratedFrom, - repository, - }); - return { octokit, repository }; - - case "different": { - const newRepository = filterPromptCancel( - await prompts.text({ - message: `What would you like to call the repository?`, - }), - ); - - if (!newRepository) { - return {}; - } - - repository = newRepository; - break; - } - - case "local": - octokit = undefined; - break; - } - } - - return { octokit, repository }; -} diff --git a/src/shared/options/exclusionKeys.ts b/src/shared/options/exclusionKeys.ts deleted file mode 100644 index 8d3b03425..000000000 --- a/src/shared/options/exclusionKeys.ts +++ /dev/null @@ -1,158 +0,0 @@ -import { Options, OptionsBase } from "../types.js"; - -export interface ExclusionDescription { - hint: string; - label: string; - level?: "common" | "minimal"; -} - -export type ExclusionKey = `exclude${string}` & keyof Options; - -export const exclusionDescriptions: Record = - { - excludeAllContributors: { - hint: "--exclude-all-contributors", - label: - "Add all-contributors to track contributions and display them in a README.md table.", - }, - excludeBuild: { - hint: "--exclude-build", - label: "Add a tsup build step to generate built output files.", - level: "minimal", - }, - excludeCompliance: { - hint: "--exclude-compliance", - label: - "Add a GitHub Actions workflow to verify that PRs match an expected format.", - level: "common", - }, - excludeLintESLint: { - hint: "--exclude-lint-eslint", - label: - "Include eslint-plugin-eslint-comment to enforce good practices around ESLint comment directives.", - level: "common", - }, - excludeLintJSDoc: { - hint: "--exclude-lint-jsdoc", - label: - "Include eslint-plugin-jsdoc to enforce good practices around JSDoc comments.", - level: "common", - }, - excludeLintJson: { - hint: "--exclude-lint-json", - label: "Apply linting and sorting to *.json and *.jsonc files.", - level: "common", - }, - excludeLintKnip: { - hint: "--exclude-lint-knip", - label: "Add Knip to detect unused files, dependencies, and code exports.", - }, - excludeLintMd: { - hint: "--exclude-lint-md", - label: "Apply linting to *.md files.", - level: "common", - }, - excludeLintPackageJson: { - hint: "--exclude-lint-package-json", - label: - "Add eslint-plugin-package-json to lint for package.json correctness.", - level: "common", - }, - excludeLintPackages: { - hint: "--exclude-lint-packages", - label: - "Add a pnpm dedupe workflow to ensure packages aren't duplicated unnecessarily.", - level: "common", - }, - excludeLintPerfectionist: { - hint: "--exclude-lint-perfectionist", - label: - "Apply eslint-plugin-perfectionist to ensure imports, keys, and so on are in sorted order.", - level: "common", - }, - excludeLintRegexp: { - hint: "--exclude-lint-regexp", - label: - "Include eslint-plugin-regexp to enforce good practices around regular expressions.", - level: "common", - }, - excludeLintSpelling: { - hint: "--exclude-lint-spelling", - label: "Add cspell to spell check against dictionaries of known words.", - level: "common", - }, - excludeLintStrict: { - hint: "--exclude-lint-strict", - label: - "Include strict logical lint rules such as typescript-eslint's strict config. ", - level: "common", - }, - excludeLintStylistic: { - hint: "--exclude-lint-stylistic", - label: - "Include stylistic lint rules such as typescript-eslint's stylistic config.", - level: "common", - }, - excludeLintYml: { - hint: "--exclude-lint-yml", - label: "Apply linting and sorting to *.yaml and *.yml files.", - level: "common", - }, - excludeReleases: { - hint: "--exclude-releases", - label: - "Add release-it to generate changelogs, package bumps, and publishes based on conventional commits.", - }, - excludeRenovate: { - hint: "--exclude-renovate", - label: "Add a Renovate config to keep dependencies up-to-date with PRs.", - }, - excludeTemplatedBy: { - hint: "--exclude-templated-by", - label: - 'Add a _"This package was templated with create-typescript-app"_ notice at the end of the README.md.', - level: "minimal", - }, - excludeTests: { - hint: "--exclude-tests", - label: - "Add Vitest tooling for fast unit tests, configured with coverage tracking.", - }, - }; - -export const exclusionKeys = Object.keys( - exclusionDescriptions, -) as ExclusionKey[]; - -export function getExclusions( - options: Partial, - base?: OptionsBase, -): Partial { - switch (base) { - case "common": - return { - ...Object.fromEntries( - exclusionKeys - .filter( - (exclusion) => - exclusionDescriptions[exclusion].level === "common", - ) - .map((exclusion) => [exclusion, options[exclusion] ?? true]), - ), - }; - case "minimal": - return { - ...Object.fromEntries( - exclusionKeys - .filter( - (exclusion) => - exclusionDescriptions[exclusion].level !== "minimal", - ) - .map((exclusion) => [exclusion, options[exclusion] ?? true]), - ), - }; - // We only really care about exclusions on the common and minimal bases - default: - return {}; - } -} diff --git a/src/shared/options/getBase.test.ts b/src/shared/options/getBase.test.ts deleted file mode 100644 index 9f50ce440..000000000 --- a/src/shared/options/getBase.test.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; - -import { getBase } from "./getBase.js"; - -const mockReadPackageData = vi.fn(); -vi.mock("../packages.js", () => ({ - get readPackageData() { - return mockReadPackageData; - }, -})); - -describe("getBase", () => { - it("should return minimal with minimal scripts", async () => { - mockReadPackageData.mockImplementationOnce(() => - Promise.resolve({ - scripts: { - build: "build", - lint: "lint", - test: "test", - }, - }), - ); - - expect(await getBase()).toBe("minimal"); - }); - - it("should return common with common scripts", async () => { - mockReadPackageData.mockImplementationOnce(() => - Promise.resolve({ - scripts: { - build: "build", - lint: "lint", - "lint:knip": "knip", - test: "test", - }, - }), - ); - - expect(await getBase()).toBe("common"); - }); - - it("should return everything with everything scripts", async () => { - mockReadPackageData.mockImplementationOnce(() => - Promise.resolve({ - scripts: { - build: "build", - lint: "lint", - "lint:knip": "knip", - "lint:md": "md", - "lint:packages": "packages", - "lint:spelling": "spelling", - test: "test", - }, - }), - ); - - expect(await getBase()).toBe("everything"); - }); -}); diff --git a/src/shared/options/getBase.ts b/src/shared/options/getBase.ts deleted file mode 100644 index af5e8bf6e..000000000 --- a/src/shared/options/getBase.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { readPackageData } from "../packages.js"; -import { OptionsBase } from "../types.js"; - -const commonScripts = new Set(["lint:knip", "test"]); - -const everythingScripts = new Set([ - "lint:md", - "lint:packages", - "lint:spelling", -]); - -export async function getBase(): Promise { - const scripts = Object.keys((await readPackageData()).scripts ?? {}); - - if ( - scripts.reduce( - (acc, curr) => (everythingScripts.has(curr) ? acc + 1 : acc), - 0, - ) >= 3 - ) { - return "everything"; - } - - if ( - scripts.reduce( - (acc, curr) => (commonScripts.has(curr) ? acc + 1 : acc), - 0, - ) >= 2 - ) { - return "common"; - } - - return "minimal"; -} diff --git a/src/shared/options/getPrefillOrPromptedOption.test.ts b/src/shared/options/getPrefillOrPromptedOption.test.ts deleted file mode 100644 index ef190dd49..000000000 --- a/src/shared/options/getPrefillOrPromptedOption.test.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { TextOptions } from "@clack/prompts"; -import { describe, expect, it, vi } from "vitest"; - -import { getPrefillOrPromptedOption } from "./getPrefillOrPromptedOption.js"; - -const mockText = vi.fn(); - -vi.mock("@clack/prompts", () => ({ - isCancel: () => false, - get text() { - return mockText; - }, -})); - -describe("getPrefillOrPromptedValue", () => { - it("returns the provided when it exists", async () => { - const value = "Test Value"; - - const actual = await getPrefillOrPromptedOption({ - auto: true, - getDefaultValue: vi.fn().mockResolvedValue("default value"), - message: "Input message.", - name: "field", - provided: value, - }); - - expect(actual).toEqual({ error: undefined, value }); - }); - - it("returns the default value when auto is true and it exists", async () => { - const value = "Test Value"; - - const actual = await getPrefillOrPromptedOption({ - auto: true, - getDefaultValue: vi.fn().mockResolvedValue(value), - message: "Input message.", - name: "field", - }); - - expect(actual).toEqual({ error: undefined, value }); - }); - - it("returns an error when auto is true and no default value exists", async () => { - const actual = await getPrefillOrPromptedOption({ - auto: true, - getDefaultValue: vi.fn().mockResolvedValue(undefined), - message: "Input message.", - name: "field", - }); - - expect(actual).toEqual({ - error: "Could not infer a default value for field.", - value: undefined, - }); - }); - - it("provides no placeholder when one is not provided and auto is false", async () => { - const message = "Test message"; - - await getPrefillOrPromptedOption({ auto: false, message, name: "field" }); - - expect(mockText).toHaveBeenCalledWith({ - message, - placeholder: undefined, - validate: expect.any(Function), - }); - }); - - it("prompts with the default value as a placeholder when a placeholder function is provided and auto is false", async () => { - const message = "Test message"; - const placeholder = "Test placeholder"; - - await getPrefillOrPromptedOption({ - auto: false, - getDefaultValue: vi.fn().mockResolvedValue(placeholder), - message, - name: "field", - }); - - expect(mockText).toHaveBeenCalledWith({ - message, - placeholder, - validate: expect.any(Function), - }); - }); - - it("validates entered text when it's not blank and auto is false", async () => { - const message = "Test message"; - - await getPrefillOrPromptedOption({ auto: false, message, name: "field" }); - - const { validate } = (mockText.mock.calls[0] as [Required])[0]; - - expect(validate(message)).toBeUndefined(); - }); - - it("invalidates entered text when it's blank and auto is false", async () => { - const message = ""; - - await getPrefillOrPromptedOption({ auto: false, message, name: "field" }); - - const { validate } = (mockText.mock.calls[0] as [Required])[0]; - - expect(validate(message)).toBe("Please enter a value."); - }); -}); diff --git a/src/shared/options/getPrefillOrPromptedOption.ts b/src/shared/options/getPrefillOrPromptedOption.ts deleted file mode 100644 index 17384ba34..000000000 --- a/src/shared/options/getPrefillOrPromptedOption.ts +++ /dev/null @@ -1,48 +0,0 @@ -import * as prompts from "@clack/prompts"; - -import { filterPromptCancel } from "../prompts.js"; - -export interface GetPrefillOrPromptedOptionOptions { - auto: boolean; - getDefaultValue?: () => Promise; - message: string; - name: string; - provided?: string | undefined; -} - -export async function getPrefillOrPromptedOption({ - auto, - getDefaultValue, - message, - name, - provided, -}: GetPrefillOrPromptedOptionOptions) { - if (provided) { - return { value: provided }; - } - - const defaultValue = await getDefaultValue?.(); - - if (auto) { - return { - error: defaultValue - ? undefined - : `Could not infer a default value for ${name}.`, - value: defaultValue, - }; - } - - return { - value: filterPromptCancel( - await prompts.text({ - message, - placeholder: defaultValue, - validate: (val) => { - if (val.length === 0) { - return "Please enter a value."; - } - }, - }), - ), - }; -} diff --git a/src/shared/options/logInferredOptions.test.ts b/src/shared/options/logInferredOptions.test.ts deleted file mode 100644 index f3a8ec9e2..000000000 --- a/src/shared/options/logInferredOptions.test.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; - -import { InferredOptions, logInferredOptions } from "./logInferredOptions.js"; - -function makeProxy(receiver: T): T { - return new Proxy(receiver, { - get: () => makeProxy((input: string) => input), - }); -} - -vi.mock("chalk", () => ({ - default: makeProxy({}), -})); - -const mockLogLine = vi.fn(); - -vi.mock("../cli/lines.js", () => ({ - get logLine() { - return mockLogLine; - }, -})); - -const options = { - description: "Test description.", - email: { - github: "github@email.com", - npm: "npm@email.com", - }, - owner: "TestOwner", - repository: "test-repository", - title: "Test Title", -} satisfies InferredOptions; - -describe("logInferredOptions", () => { - it("logs the required inferred values when only they exist", () => { - logInferredOptions(options); - - expect(mockLogLine.mock.calls).toMatchInlineSnapshot(` - [ - [], - [ - "--auto inferred the following values:", - ], - [ - "- description: Test description.", - ], - [ - "- email-github: github@email.com", - ], - [ - "- email-npm: github@email.com", - ], - [ - "- owner: TestOwner", - ], - [ - "- repository: test-repository", - ], - [ - "- title: Test Title", - ], - ] - `); - }); - - it("logs additional and required inferred values when all they exist", () => { - logInferredOptions({ - ...options, - guide: { - href: "https://example.com/guide", - title: "Example Guide", - }, - logo: { - alt: "Logo text.", - src: "https://example.com/logo", - }, - }); - - expect(mockLogLine.mock.calls).toMatchInlineSnapshot(` - [ - [], - [ - "--auto inferred the following values:", - ], - [ - "- description: Test description.", - ], - [ - "- email-github: github@email.com", - ], - [ - "- email-npm: github@email.com", - ], - [ - "- guide: https://example.com/guide", - ], - [ - "- guide-title: Example Guide", - ], - [ - "- logo: https://example.com/logo", - ], - [ - "- logo-alt: Logo text.", - ], - [ - "- owner: TestOwner", - ], - [ - "- repository: test-repository", - ], - [ - "- title: Test Title", - ], - ] - `); - }); -}); diff --git a/src/shared/options/logInferredOptions.ts b/src/shared/options/logInferredOptions.ts deleted file mode 100644 index 833d25c78..000000000 --- a/src/shared/options/logInferredOptions.ts +++ /dev/null @@ -1,39 +0,0 @@ -import chalk from "chalk"; - -import { logLine } from "../cli/lines.js"; -import { Options } from "../types.js"; - -export type InferredOptions = Pick< - Options, - "description" | "email" | "guide" | "logo" | "owner" | "repository" | "title" ->; - -export function logInferredOptions(augmentedOptions: InferredOptions) { - logLine(); - logLine(chalk.gray("--auto inferred the following values:")); - logLine(chalk.gray(`- description: ${augmentedOptions.description}`)); - logLine(chalk.gray(`- email-github: ${augmentedOptions.email.github}`)); - logLine(chalk.gray(`- email-npm: ${augmentedOptions.email.github}`)); - - if (augmentedOptions.guide) { - logLine(chalk.gray(`- guide: ${augmentedOptions.guide.href}`)); - logLine(chalk.gray(`- guide-title: ${augmentedOptions.guide.title}`)); - } - - if (augmentedOptions.logo) { - logLine(chalk.gray(`- logo: ${augmentedOptions.logo.src}`)); - logLine(chalk.gray(`- logo-alt: ${augmentedOptions.logo.alt}`)); - - if (augmentedOptions.logo.height) { - logLine(chalk.gray(`- logo-height: ${augmentedOptions.logo.height}`)); - } - - if (augmentedOptions.logo.width) { - logLine(chalk.gray(`- logo-width: ${augmentedOptions.logo.width}`)); - } - } - - logLine(chalk.gray(`- owner: ${augmentedOptions.owner}`)); - logLine(chalk.gray(`- repository: ${augmentedOptions.repository}`)); - logLine(chalk.gray(`- title: ${augmentedOptions.title}`)); -} diff --git a/src/shared/options/optionsSchema.ts b/src/shared/options/optionsSchema.ts deleted file mode 100644 index 3ac1aa70c..000000000 --- a/src/shared/options/optionsSchema.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { z } from "zod"; - -export const optionsSchemaShape = { - access: z.union([z.literal("public"), z.literal("restricted")]).optional(), - author: z.string().optional(), - auto: z.boolean().optional(), - base: z - .union([ - z.literal("common"), - z.literal("everything"), - z.literal("minimal"), - z.literal("prompt"), - ]) - .optional(), - bin: z.string().optional(), - description: z.string().optional(), - directory: z.string().optional(), - email: z - .object({ - github: z.string().email(), - npm: z.string().email(), - }) - .optional(), - excludeAllContributors: z.boolean().optional(), - excludeBuild: z.boolean().optional(), - excludeCompliance: z.boolean().optional(), - excludeLintESLint: z.boolean().optional(), - excludeLintJSDoc: z.boolean().optional(), - excludeLintJson: z.boolean().optional(), - excludeLintKnip: z.boolean().optional(), - excludeLintMd: z.boolean().optional(), - excludeLintPackageJson: z.boolean().optional(), - excludeLintPackages: z.boolean().optional(), - excludeLintPerfectionist: z.boolean().optional(), - excludeLintRegexp: z.boolean().optional(), - excludeLintSpelling: z.boolean().optional(), - excludeLintStrict: z.boolean().optional(), - excludeLintStylistic: z.boolean().optional(), - excludeLintYml: z.boolean().optional(), - excludeReleases: z.boolean().optional(), - excludeRenovate: z.boolean().optional(), - excludeTemplatedBy: z.boolean().optional(), - excludeTests: z.boolean().optional(), - funding: z.string().optional(), - guide: z.string().url().optional(), - guideTitle: z.string().optional(), - keywords: z.array(z.string()).optional(), - logo: z.string().optional(), - logoAlt: z.string().optional(), - mode: z - .union([z.literal("create"), z.literal("initialize"), z.literal("migrate")]) - .optional(), - offline: z.boolean().optional(), - owner: z.string().optional(), - preserveGeneratedFrom: z.boolean().optional(), - repository: z.string().optional(), - skipAllContributorsApi: z.boolean().optional(), - skipGitHubApi: z.boolean().optional(), - skipInstall: z.boolean().optional(), - skipRemoval: z.boolean().optional(), - skipRestore: z.boolean().optional(), - skipUninstall: z.boolean().optional(), - title: z.string().optional(), -}; - -export const optionsSchema = z.object(optionsSchemaShape); diff --git a/src/shared/options/readOptions.test.ts b/src/shared/options/readOptions.test.ts deleted file mode 100644 index e9c741bfc..000000000 --- a/src/shared/options/readOptions.test.ts +++ /dev/null @@ -1,843 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; -import z from "zod"; - -import { Options } from "../types.js"; -import { GetPrefillOrPromptedOptionOptions } from "./getPrefillOrPromptedOption.js"; -import { optionsSchemaShape } from "./optionsSchema.js"; -import { readOptions } from "./readOptions.js"; - -const emptyOptions = { - access: undefined, - author: undefined, - auto: false, - base: undefined, - bin: undefined, - description: undefined, - directory: undefined, - email: undefined, - excludeAllContributors: undefined, - excludeCompliance: undefined, - excludeLintESLint: undefined, - excludeLintJSDoc: undefined, - excludeLintJson: undefined, - excludeLintKnip: undefined, - excludeLintMd: undefined, - excludeLintPackageJson: undefined, - excludeLintPackages: undefined, - excludeLintPerfectionist: undefined, - excludeLintRegexp: undefined, - excludeLintSpelling: undefined, - excludeLintStrict: undefined, - excludeLintYml: undefined, - excludeReleases: undefined, - excludeRenovate: undefined, - excludeTemplatedBy: undefined, - excludeTests: undefined, - funding: undefined, - guide: undefined, - guideTitle: undefined, - logo: undefined, - logoAlt: undefined, - offline: undefined, - owner: undefined, - preserveGeneratedFrom: false, - repository: undefined, - skipAllContributorsApi: undefined, - skipGitHubApi: undefined, - skipInstall: undefined, - skipRemoval: undefined, - skipRestore: false, - skipUninstall: undefined, - title: undefined, -}; - -const mockOptions = { - base: "prompt", - github: "mock.git", - repository: "mock.repository", -}; - -vi.mock("../cli/spinners.ts", () => ({ - withSpinner() { - return () => ({}); - }, -})); - -const mockReadPackageData = vi.fn().mockResolvedValue({}); - -vi.mock("../packages.js", () => ({ - get readPackageData() { - return mockReadPackageData; - }, -})); - -const mockAugmentOptionsWithExcludes = vi.fn(); - -vi.mock("./augmentOptionsWithExcludes.js", () => ({ - get augmentOptionsWithExcludes() { - return mockAugmentOptionsWithExcludes; - }, -})); - -const mockDetectEmailRedundancy = vi.fn(); - -vi.mock("./detectEmailRedundancy.js", () => ({ - get detectEmailRedundancy() { - return mockDetectEmailRedundancy; - }, -})); - -const mockGetPrefillOrPromptedOption = vi.fn(); - -vi.mock("./getPrefillOrPromptedOption.js", () => ({ - get getPrefillOrPromptedOption() { - return mockGetPrefillOrPromptedOption; - }, -})); - -const mockEnsureRepositoryExists = vi.fn(); - -vi.mock("./ensureRepositoryExists.js", () => ({ - get ensureRepositoryExists() { - return mockEnsureRepositoryExists; - }, -})); - -vi.mock("./getGitHub.js", () => ({ - getGitHub() { - return undefined; - }, -})); - -vi.mock("./createOptionDefaults/index.js", () => ({ - createOptionDefaults() { - return { - author: vi.fn(), - bin: vi.fn(), - description: vi.fn(), - email: vi.fn(), - funding: vi.fn(), - guide: vi.fn(), - logo: vi.fn(), - owner: vi.fn(), - repository: vi.fn(), - title: vi.fn(), - }; - }, -})); - -const mockLogInferredOptions = vi.fn(); - -vi.mock("./logInferredOptions.js", () => ({ - get logInferredOptions() { - return mockLogInferredOptions; - }, -})); - -describe("readOptions", () => { - it("returns a cancellation when an arg is invalid", async () => { - const validationResult = z - .object({ base: optionsSchemaShape.base }) - .safeParse({ base: "b" }); - - const actual = await readOptions(["--base", "b"], "migrate"); - - expect(actual).toStrictEqual({ - cancelled: true, - error: (validationResult as z.SafeParseError<{ base: string }>).error, - options: { ...emptyOptions, base: "b" }, - }); - }); - - it("returns a cancellation when an email redundancy is detected", async () => { - const error = "Too many emails!"; - mockDetectEmailRedundancy.mockReturnValue(error); - mockGetPrefillOrPromptedOption.mockImplementation(() => ({ - value: undefined, - })); - - expect(await readOptions([], "migrate")).toStrictEqual({ - cancelled: true, - error, - options: { - ...emptyOptions, - base: "minimal", - }, - }); - }); - - it("returns a cancellation when the owner prompt is cancelled", async () => { - mockDetectEmailRedundancy.mockReturnValue(false); - mockGetPrefillOrPromptedOption.mockImplementation(() => ({ - value: undefined, - })); - - expect(await readOptions([], "migrate")).toStrictEqual({ - cancelled: true, - error: undefined, - options: { - ...emptyOptions, - base: "minimal", - }, - }); - }); - - it("returns a cancellation when the repository prompt is cancelled", async () => { - mockDetectEmailRedundancy.mockReturnValue(false); - mockGetPrefillOrPromptedOption - .mockImplementationOnce(() => ({ value: "MockOwner" })) - .mockImplementation(() => ({ value: undefined })); - - expect(await readOptions([], "migrate")).toStrictEqual({ - cancelled: true, - error: undefined, - options: { - ...emptyOptions, - base: "minimal", - owner: "MockOwner", - }, - }); - }); - - it("returns a cancellation when ensureRepositoryPrompt does not return a repository", async () => { - mockDetectEmailRedundancy.mockReturnValue(false); - mockGetPrefillOrPromptedOption - .mockImplementationOnce(() => ({ value: "MockOwner" })) - .mockImplementationOnce(() => ({ value: "MockRepository" })) - .mockImplementation(() => ({ value: undefined })); - mockEnsureRepositoryExists.mockResolvedValue({}); - - expect(await readOptions([], "migrate")).toStrictEqual({ - cancelled: true, - error: undefined, - options: { - ...emptyOptions, - base: "minimal", - owner: "MockOwner", - repository: "MockRepository", - }, - }); - }); - - it("returns a cancellation when the description prompt is cancelled", async () => { - mockDetectEmailRedundancy.mockReturnValue(false); - mockGetPrefillOrPromptedOption - .mockImplementationOnce(() => ({ value: "MockOwner" })) - .mockImplementationOnce(() => ({ value: "MockRepository" })) - .mockImplementation(() => ({ value: undefined })); - mockEnsureRepositoryExists.mockResolvedValue({ - octokit: undefined, - repository: mockOptions.repository, - }); - - expect(await readOptions([], "migrate")).toStrictEqual({ - cancelled: true, - error: undefined, - options: { - ...emptyOptions, - base: "minimal", - owner: "MockOwner", - repository: "MockRepository", - }, - }); - }); - - it("returns a cancellation when the title prompt is cancelled", async () => { - mockDetectEmailRedundancy.mockReturnValue(false); - mockGetPrefillOrPromptedOption - .mockImplementationOnce(() => ({ value: "MockOwner" })) - .mockImplementationOnce(() => ({ value: "MockRepository" })) - .mockImplementationOnce(() => ({ value: "Mock description." })) - .mockImplementation(() => ({ value: undefined })); - mockEnsureRepositoryExists.mockResolvedValue({ - octokit: undefined, - repository: mockOptions.repository, - }); - - expect(await readOptions([], "migrate")).toStrictEqual({ - cancelled: true, - error: undefined, - options: { - ...emptyOptions, - base: "minimal", - description: "Mock description.", - owner: "MockOwner", - repository: "MockRepository", - }, - }); - }); - - it("returns a cancellation when the guide title prompt is cancelled", async () => { - mockDetectEmailRedundancy.mockReturnValue(false); - mockGetPrefillOrPromptedOption - .mockImplementationOnce(() => ({ value: "MockOwner" })) - .mockImplementationOnce(() => ({ value: "MockRepository" })) - .mockImplementationOnce(() => ({ value: "Mock description." })) - .mockImplementationOnce(() => ({ value: "Mock Title" })) - .mockImplementation(() => ({ value: undefined })); - mockEnsureRepositoryExists.mockResolvedValue({ - octokit: undefined, - repository: mockOptions.repository, - }); - - expect( - await readOptions(["--guide", "https://example.com"], "migrate"), - ).toStrictEqual({ - cancelled: true, - error: undefined, - options: { - ...emptyOptions, - base: "minimal", - description: "Mock description.", - guide: "https://example.com", - owner: "MockOwner", - repository: "MockRepository", - title: "Mock Title", - }, - }); - }); - - it("returns a cancellation when the guide alt prompt is cancelled", async () => { - mockDetectEmailRedundancy.mockReturnValue(false); - mockGetPrefillOrPromptedOption - .mockImplementationOnce(() => ({ value: "MockOwner" })) - .mockImplementationOnce(() => ({ value: "MockRepository" })) - .mockImplementationOnce(() => ({ value: "Mock description." })) - .mockImplementationOnce(() => ({ value: "Mock Title" })) - .mockImplementation(() => ({ value: undefined })); - mockEnsureRepositoryExists.mockResolvedValue({ - octokit: undefined, - repository: mockOptions.repository, - }); - - expect( - await readOptions(["--guide", "https://example.com"], "migrate"), - ).toStrictEqual({ - cancelled: true, - error: undefined, - options: { - ...emptyOptions, - base: "minimal", - description: "Mock description.", - guide: "https://example.com", - owner: "MockOwner", - repository: "MockRepository", - title: "Mock Title", - }, - }); - }); - - it("returns a cancellation when the logo alt prompt is cancelled", async () => { - mockDetectEmailRedundancy.mockReturnValue(false); - mockGetPrefillOrPromptedOption - .mockImplementationOnce(() => ({ value: "MockOwner" })) - .mockImplementationOnce(() => ({ value: "MockRepository" })) - .mockImplementationOnce(() => ({ value: "Mock description." })) - .mockImplementation(() => ({ value: undefined })); - mockEnsureRepositoryExists.mockResolvedValue({ - octokit: undefined, - repository: mockOptions.repository, - }); - - expect(await readOptions(["--logo", "logo.svg"], "migrate")).toStrictEqual({ - cancelled: true, - error: undefined, - options: { - ...emptyOptions, - base: "minimal", - description: "Mock description.", - logo: "logo.svg", - owner: "MockOwner", - repository: "MockRepository", - }, - }); - }); - - it("returns a cancellation when the email prompt is cancelled", async () => { - mockDetectEmailRedundancy.mockReturnValue(false); - mockGetPrefillOrPromptedOption - .mockImplementationOnce(() => ({ value: "MockOwner" })) - .mockImplementationOnce(() => ({ value: "MockRepository" })) - .mockImplementationOnce(() => ({ value: "Mock description." })) - .mockImplementationOnce(() => ({ value: "Mock title." })) - .mockImplementation(() => ({ value: undefined })); - mockEnsureRepositoryExists.mockResolvedValue({ - octokit: undefined, - repository: mockOptions.repository, - }); - - expect(await readOptions([], "migrate")).toStrictEqual({ - cancelled: true, - error: undefined, - options: { - ...emptyOptions, - base: "minimal", - description: "Mock description.", - owner: "MockOwner", - repository: "MockRepository", - title: "Mock title.", - }, - }); - }); - - it("returns a cancellation when augmentOptionsWithExcludes returns undefined", async () => { - mockDetectEmailRedundancy.mockReturnValue(false); - mockGetPrefillOrPromptedOption - .mockImplementationOnce(() => ({ value: "MockOwner" })) - .mockImplementationOnce(() => ({ value: "MockRepository" })) - .mockImplementationOnce(() => ({ value: "Mock description." })) - .mockImplementationOnce(() => ({ value: "Mock title." })) - .mockImplementation(() => ({ value: undefined })); - mockEnsureRepositoryExists.mockResolvedValue({ - octokit: undefined, - repository: mockOptions.repository, - }); - mockAugmentOptionsWithExcludes.mockResolvedValue(undefined); - - expect(await readOptions([], "migrate")).toStrictEqual({ - cancelled: true, - error: undefined, - options: { - ...emptyOptions, - base: "minimal", - description: "Mock description.", - owner: "MockOwner", - repository: "MockRepository", - title: "Mock title.", - }, - }); - }); - - it("returns success options when --base is valid", async () => { - mockAugmentOptionsWithExcludes.mockResolvedValue({ - ...emptyOptions, - ...mockOptions, - }); - mockGetPrefillOrPromptedOption.mockImplementation(() => ({ - value: "mock", - })); - mockEnsureRepositoryExists.mockResolvedValue({ - octokit: undefined, - repository: mockOptions.repository, - }); - - expect( - await readOptions(["--base", mockOptions.base], "migrate"), - ).toStrictEqual({ - cancelled: false, - octokit: undefined, - options: { - ...emptyOptions, - ...mockOptions, - }, - }); - }); - - it("returns success options when --base is valid with all optional options", async () => { - mockAugmentOptionsWithExcludes.mockResolvedValue({ - ...emptyOptions, - ...mockOptions, - }); - mockGetPrefillOrPromptedOption.mockImplementation(() => ({ - value: "mock", - })); - mockEnsureRepositoryExists.mockResolvedValue({ - octokit: undefined, - repository: mockOptions.repository, - }); - - expect( - await readOptions( - [ - "--base", - mockOptions.base, - "--guide", - "https://example.com", - "--logo", - "logo.svg", - ], - "migrate", - ), - ).toStrictEqual({ - cancelled: false, - octokit: undefined, - options: { - ...emptyOptions, - ...mockOptions, - }, - }); - }); - - it("returns cancelled options when augmentOptionsWithExcludes returns undefined", async () => { - mockAugmentOptionsWithExcludes.mockResolvedValue(undefined); - mockGetPrefillOrPromptedOption.mockImplementation(() => ({ - value: "mock", - })); - - expect( - await readOptions(["--base", mockOptions.base], "migrate"), - ).toStrictEqual({ - cancelled: true, - options: { - ...emptyOptions, - base: mockOptions.base, - description: "mock", - owner: "mock", - repository: "mock", - title: "mock", - }, - }); - }); - - it("defaults preserveGeneratedFrom to false when the owner is not JoshuaKGoldberg", async () => { - mockAugmentOptionsWithExcludes.mockImplementationOnce( - (options: Partial) => ({ - ...options, - ...mockOptions, - }), - ); - mockEnsureRepositoryExists.mockResolvedValue({ - octokit: undefined, - repository: mockOptions.repository, - }); - mockGetPrefillOrPromptedOption.mockImplementation(() => ({ - value: "mock", - })); - - expect( - await readOptions(["--base", mockOptions.base], "migrate"), - ).toStrictEqual({ - cancelled: false, - octokit: undefined, - options: expect.objectContaining({ - preserveGeneratedFrom: false, - }), - }); - }); - - it("defaults preserveGeneratedFrom to true when the owner is JoshuaKGoldberg", async () => { - mockAugmentOptionsWithExcludes.mockImplementationOnce( - (options: Partial) => ({ - ...options, - ...mockOptions, - }), - ); - mockEnsureRepositoryExists.mockResolvedValue({ - octokit: undefined, - repository: mockOptions.repository, - }); - mockGetPrefillOrPromptedOption.mockImplementation(() => ({ - value: "mock", - })); - - expect( - await readOptions( - ["--base", mockOptions.base, "--owner", "JoshuaKGoldberg"], - "migrate", - ), - ).toStrictEqual({ - cancelled: false, - octokit: undefined, - options: expect.objectContaining({ - preserveGeneratedFrom: true, - }), - }); - }); - - it("defaults skipRestore to false when the mode is not create", async () => { - mockAugmentOptionsWithExcludes.mockImplementationOnce( - (options: Partial) => ({ - ...options, - ...mockOptions, - }), - ); - mockEnsureRepositoryExists.mockResolvedValue({ - octokit: undefined, - repository: mockOptions.repository, - }); - mockGetPrefillOrPromptedOption.mockImplementation(() => ({ - value: "mock", - })); - - expect( - await readOptions(["--base", mockOptions.base], "migrate"), - ).toStrictEqual({ - cancelled: false, - octokit: undefined, - options: expect.objectContaining({ - skipRestore: false, - }), - }); - }); - - it("defaults skipRestore to true when the mode is create", async () => { - mockAugmentOptionsWithExcludes.mockImplementationOnce( - (options: Partial) => ({ - ...options, - ...mockOptions, - }), - ); - mockEnsureRepositoryExists.mockResolvedValue({ - octokit: undefined, - repository: mockOptions.repository, - }); - mockGetPrefillOrPromptedOption.mockImplementation(() => ({ - value: "mock", - })); - - expect( - await readOptions(["--base", mockOptions.base], "create"), - ).toStrictEqual({ - cancelled: false, - octokit: undefined, - options: expect.objectContaining({ - skipRestore: true, - }), - }); - }); - - it("skips API calls when --offline is true", async () => { - mockAugmentOptionsWithExcludes.mockImplementation((options: Options) => ({ - ...emptyOptions, - ...mockOptions, - ...options, - })); - mockGetPrefillOrPromptedOption.mockImplementation(() => ({ - value: "mock", - })); - mockEnsureRepositoryExists.mockResolvedValue({ - octokit: undefined, - repository: mockOptions.repository, - }); - - expect( - await readOptions(["--base", mockOptions.base, "--offline"], "migrate"), - ).toStrictEqual({ - cancelled: false, - octokit: undefined, - options: { - ...emptyOptions, - ...mockOptions, - access: "public", - description: "mock", - directory: "mock", - email: { - github: "mock", - npm: "mock", - }, - guide: undefined, - logo: undefined, - mode: "migrate", - offline: true, - owner: "mock", - skipAllContributorsApi: true, - skipGitHubApi: true, - title: "mock", - }, - }); - }); - - it("infers base from package scripts during migration", async () => { - mockReadPackageData.mockImplementationOnce(() => - Promise.resolve({ - scripts: { - build: "build", - lint: "lint", - test: "test", - }, - }), - ); - expect(await readOptions(["--offline"], "migrate")).toStrictEqual({ - cancelled: false, - octokit: undefined, - options: { - ...emptyOptions, - ...mockOptions, - access: "public", - base: "minimal", - description: "mock", - directory: "mock", - email: { - github: "mock", - npm: "mock", - }, - guide: undefined, - logo: undefined, - mode: "migrate", - offline: true, - owner: "mock", - skipAllContributorsApi: true, - skipGitHubApi: true, - title: "mock", - }, - }); - }); - - it("uses values when provided on the CLI", async () => { - mockReadPackageData.mockImplementationOnce(() => ({})); - mockGetPrefillOrPromptedOption.mockImplementation( - async ({ getDefaultValue }: GetPrefillOrPromptedOptionOptions) => ({ - value: (await getDefaultValue?.()) ?? "mock", - }), - ); - - const description = "Test description."; - const owner = "TestOwner"; - const repository = "test-repository"; - const title = "Test Title"; - - expect( - await readOptions( - [ - "--offline", - "--description", - description, - "--owner", - owner, - "--repository", - repository, - "--title", - title, - ], - "migrate", - ), - ).toStrictEqual({ - cancelled: false, - octokit: undefined, - options: { - ...emptyOptions, - ...mockOptions, - access: "public", - base: "minimal", - description, - directory: repository, - email: { - github: "mock", - npm: "mock", - }, - guide: undefined, - logo: undefined, - mode: "migrate", - offline: true, - owner, - skipAllContributorsApi: true, - skipGitHubApi: true, - title, - }, - }); - - expect(mockLogInferredOptions).not.toHaveBeenCalled(); - }); - - it("uses and logs values when provided on the CLI with auto", async () => { - mockReadPackageData.mockImplementationOnce(() => ({})); - mockGetPrefillOrPromptedOption.mockImplementation( - async ({ getDefaultValue }: GetPrefillOrPromptedOptionOptions) => ({ - value: (await getDefaultValue?.()) ?? "mock", - }), - ); - - const description = "Test description."; - const owner = "TestOwner"; - const repository = "test-repository"; - const title = "Test Title"; - - expect( - await readOptions( - [ - "--auto", - "--offline", - "--description", - description, - "--owner", - owner, - "--repository", - repository, - "--title", - title, - ], - "migrate", - ), - ).toStrictEqual({ - cancelled: false, - octokit: undefined, - options: { - ...emptyOptions, - ...mockOptions, - access: "public", - auto: true, - base: "minimal", - description, - directory: repository, - email: { - github: "mock", - npm: "mock", - }, - guide: undefined, - logo: undefined, - mode: "migrate", - offline: true, - owner, - skipAllContributorsApi: true, - skipGitHubApi: true, - title, - }, - }); - - expect(mockLogInferredOptions.mock.calls).toMatchInlineSnapshot(` - [ - [ - { - "access": "public", - "author": undefined, - "auto": true, - "base": "minimal", - "bin": undefined, - "description": "Test description.", - "directory": "test-repository", - "email": { - "github": "mock", - "npm": "mock", - }, - "excludeAllContributors": undefined, - "excludeCompliance": undefined, - "excludeLintESLint": undefined, - "excludeLintJSDoc": undefined, - "excludeLintJson": undefined, - "excludeLintKnip": undefined, - "excludeLintMd": undefined, - "excludeLintPackageJson": undefined, - "excludeLintPackages": undefined, - "excludeLintPerfectionist": undefined, - "excludeLintRegexp": undefined, - "excludeLintSpelling": undefined, - "excludeLintStrict": undefined, - "excludeLintYml": undefined, - "excludeReleases": undefined, - "excludeRenovate": undefined, - "excludeTemplatedBy": undefined, - "excludeTests": undefined, - "funding": undefined, - "github": "mock.git", - "guide": undefined, - "guideTitle": undefined, - "logo": undefined, - "logoAlt": undefined, - "mode": "migrate", - "offline": true, - "owner": "TestOwner", - "preserveGeneratedFrom": false, - "repository": "mock.repository", - "skipAllContributorsApi": true, - "skipGitHubApi": true, - "skipInstall": undefined, - "skipRemoval": undefined, - "skipRestore": false, - "skipUninstall": undefined, - "title": "Test Title", - }, - ], - ] - `); - }); -}); diff --git a/src/shared/options/readOptions.ts b/src/shared/options/readOptions.ts deleted file mode 100644 index 181bde21e..000000000 --- a/src/shared/options/readOptions.ts +++ /dev/null @@ -1,305 +0,0 @@ -import { parseArgs } from "node:util"; -import { Octokit } from "octokit"; -import { octokitFromAuth } from "octokit-from-auth"; -import { titleCase } from "title-case"; -import { z } from "zod"; - -import { withSpinner } from "../cli/spinners.js"; -import { Mode, OptionsGuide, PromptedOptions } from "../types.js"; -import { Options, OptionsLogo } from "../types.js"; -import { allArgOptions } from "./args.js"; -import { augmentOptionsWithExcludes } from "./augmentOptionsWithExcludes.js"; -import { createOptionDefaults } from "./createOptionDefaults/index.js"; -import { detectEmailRedundancy } from "./detectEmailRedundancy.js"; -import { ensureRepositoryExists } from "./ensureRepositoryExists.js"; -import { getBase } from "./getBase.js"; -import { getPrefillOrPromptedOption } from "./getPrefillOrPromptedOption.js"; -import { logInferredOptions } from "./logInferredOptions.js"; -import { optionsSchema } from "./optionsSchema.js"; -import { readLogoSizing } from "./readLogoSizing.js"; - -export interface OctokitAndOptions { - octokit: Octokit | undefined; - options: Options; -} - -export interface OptionsParseCancelled { - cancelled: true; - error?: string | z.ZodError; - options: object; -} - -export type OptionsParseResult = OptionsParseCancelled | OptionsParseSuccess; - -export interface OptionsParseSuccess extends OctokitAndOptions { - cancelled: false; -} - -export async function readOptions( - args: string[], - mode: Mode, - promptedOptions: PromptedOptions = {}, -): Promise { - const defaults = createOptionDefaults(promptedOptions); - const { values } = parseArgs({ - args, - options: allArgOptions, - strict: false, - tokens: true, - }); - - if (mode === "migrate" && !values.base) { - values.base = await getBase(); - } - - const mappedOptions = { - access: values.access, - author: values.author, - auto: !!values.auto, - base: values.base, - bin: values.bin, - description: values.description, - directory: values.directory, - email: - (values.email ?? values["email-github"] ?? values["email-npm"]) - ? { - github: values.email ?? values["email-github"], - npm: values.email ?? values["email-npm"], - } - : undefined, - excludeAllContributors: values["exclude-all-contributors"], - excludeCompliance: values["exclude-compliance"], - excludeLintESLint: values["exclude-lint-eslint"], - excludeLintJSDoc: values["exclude-lint-jsdoc"], - excludeLintJson: values["exclude-lint-json"], - excludeLintKnip: values["exclude-lint-knip"], - excludeLintMd: values["exclude-lint-md"], - excludeLintPackageJson: values["exclude-lint-package-json"], - excludeLintPackages: values["exclude-lint-packages"], - excludeLintPerfectionist: values["exclude-lint-perfectionist"], - excludeLintRegexp: values["exclude-lint-regexp"], - excludeLintSpelling: values["exclude-lint-spelling"], - excludeLintStrict: values["exclude-lint-strict"], - excludeLintYml: values["exclude-lint-yml"], - excludeReleases: values["exclude-releases"], - excludeRenovate: values["exclude-renovate"], - excludeTemplatedBy: values["exclude-templated-by"], - excludeTests: values["unit-tests"], - funding: values.funding, - guide: values.guide, - guideTitle: values["guide-title"], - logo: values.logo, - logoAlt: values["logo-alt"], - offline: values.offline, - owner: values.owner, - preserveGeneratedFrom: - values["preserve-generated-from"] ?? values.owner === "JoshuaKGoldberg", - repository: values.repository, - skipAllContributorsApi: - values["skip-all-contributors-api"] ?? values.offline, - skipGitHubApi: values["skip-github-api"] ?? values.offline, - skipInstall: values["skip-install"], - skipRemoval: values["skip-removal"], - skipRestore: values["skip-restore"] ?? mode === "create", - skipUninstall: values["skip-uninstall"], - title: values.title, - }; - - const emailError = detectEmailRedundancy(values); - if (emailError) { - return { - cancelled: true, - error: emailError, - options: mappedOptions, - }; - } - - const optionsParseResult = optionsSchema.safeParse(mappedOptions); - - if (!optionsParseResult.success) { - return { - cancelled: true, - error: optionsParseResult.error, - options: mappedOptions, - }; - } - - const options = optionsParseResult.data; - - const ownerOption = await getPrefillOrPromptedOption({ - auto: !!mappedOptions.auto, - getDefaultValue: defaults.owner, - message: "What organization or user will the repository be under?", - name: "owner", - provided: options.owner, - }); - - options.owner ??= ownerOption.value; - - if (!options.owner) { - return { - cancelled: true, - error: ownerOption.error, - options, - }; - } - - const repositoryOption = await getPrefillOrPromptedOption({ - auto: !!mappedOptions.auto, - getDefaultValue: defaults.repository, - message: "What will the kebab-case name of the repository be?", - name: "repository", - provided: options.repository, - }); - - options.repository ??= repositoryOption.value; - - if (!options.repository) { - return { - cancelled: true, - error: repositoryOption.error, - options, - }; - } - - const { octokit, repository } = await ensureRepositoryExists( - options.skipGitHubApi - ? undefined - : await withSpinner("Checking GitHub authentication", async () => - octokitFromAuth(), - ), - { - mode, - owner: options.owner, - repository: options.repository, - }, - ); - - if (!repository) { - return { cancelled: true, error: repositoryOption.error, options }; - } - - const descriptionOption = await getPrefillOrPromptedOption({ - auto: !!mappedOptions.auto, - getDefaultValue: async () => - (await defaults.description()) ?? "A very lovely package. Hooray! 💖", - message: "How would you describe the new package?", - name: "description", - provided: options.description, - }); - - options.description ??= descriptionOption.value; - - if (!options.description) { - return { cancelled: true, error: descriptionOption.error, options }; - } - - const titleOption = await getPrefillOrPromptedOption({ - auto: !!mappedOptions.auto, - getDefaultValue: async () => - (await defaults.title()) ?? titleCase(repository).replaceAll("-", " "), - message: "What will the Title Case title of the repository be?", - name: "title", - provided: options.title, - }); - - options.title ??= titleOption.value; - - if (!options.title) { - return { cancelled: true, error: titleOption.error, options }; - } - - let guide: OptionsGuide | undefined; - - if (options.guide) { - if (options.guideTitle) { - guide = { href: options.guide, title: options.guideTitle }; - } else { - const titleOption = await getPrefillOrPromptedOption({ - auto: !!mappedOptions.auto, - message: "What is the title text for the guide?", - name: "getPrefillOrPromptedOption", - }); - - if (!titleOption.value) { - return { cancelled: true, error: titleOption.error, options }; - } - - guide = { href: options.guide, title: titleOption.value }; - } - } - - let logo: OptionsLogo | undefined; - - if (options.logo) { - if (options.logoAlt) { - logo = { alt: options.logoAlt, src: options.logo }; - } else { - const logoAltOption = await getPrefillOrPromptedOption({ - auto: !!mappedOptions.auto, - message: "What is the alt text (non-visual description) of the logo?", - name: "getPrefillOrPromptedOption", - }); - - if (!logoAltOption.value) { - return { cancelled: true, error: logoAltOption.error, options }; - } - - logo = { alt: logoAltOption.value, src: options.logo }; - } - - Object.assign(logo, readLogoSizing(logo.src)); - } - - let email = options.email ?? (await defaults.email()); - - if (!email) { - const emailOption = await getPrefillOrPromptedOption({ - auto: !!mappedOptions.auto, - message: "What email should be used in package.json and .md files?", - name: "email", - }); - - if (!emailOption.value) { - return { cancelled: true, error: emailOption.error, options }; - } - - email = { github: emailOption.value, npm: emailOption.value }; - } - - const augmentedOptions = await augmentOptionsWithExcludes({ - ...options, - access: options.access ?? "public", - author: - options.author ?? (await defaults.author()) ?? (await defaults.owner()), - bin: options.bin ?? (await defaults.bin()), - description: options.description, - directory: - options.directory ?? promptedOptions.directory ?? options.repository, - email, - funding: options.funding ?? (await defaults.funding()), - guide: guide ?? (await defaults.guide()), - logo: logo ?? (await defaults.logo()), - mode, - owner: options.owner, - repository, - title: options.title, - }); - - if (!augmentedOptions) { - return { - cancelled: true, - options, - }; - } - - if (options.auto) { - logInferredOptions(augmentedOptions); - } - - return { - cancelled: false, - octokit, - options: augmentedOptions, - }; -} diff --git a/src/shared/packages.test.ts b/src/shared/packages.test.ts deleted file mode 100644 index c8d614414..000000000 --- a/src/shared/packages.test.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; - -import { removeDependencies } from "./packages.js"; - -const mockExecaCommand = vi.fn(); - -vi.mock("execa", () => ({ - get execaCommand() { - return mockExecaCommand; - }, -})); - -describe("removeDependencies", () => { - it("removes all packages that already exist when all already exist", async () => { - await removeDependencies(["one", "two"], { - one: "1.2.3", - two: "4.5.6", - }); - - expect(mockExecaCommand.mock.calls).toMatchInlineSnapshot(` - [ - [ - "pnpm remove one two", - ], - ] - `); - }); - - it("removes only packages that already exist when some don't exist", async () => { - await removeDependencies(["exists", "missing"], { - exists: "1.2.3", - }); - - expect(mockExecaCommand.mock.calls).toMatchInlineSnapshot(` - [ - [ - "pnpm remove exists", - ], - ] - `); - }); - - it("adds a flag to removing packages when one is provided", async () => { - await removeDependencies( - ["exists", "missing"], - { - exists: "1.2.3", - }, - "-D", - ); - - expect(mockExecaCommand.mock.calls).toMatchInlineSnapshot(` - [ - [ - "pnpm remove exists -D", - ], - ] - `); - }); - - it("does nothing when no packages already exist", async () => { - await removeDependencies(["missing"]); - - expect(mockExecaCommand.mock.calls).toMatchInlineSnapshot("[]"); - }); -}); diff --git a/src/shared/packages.ts b/src/shared/packages.ts deleted file mode 100644 index 21e991fc4..000000000 --- a/src/shared/packages.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { execaCommand } from "execa"; - -import { readFileSafe } from "./readFileSafe.js"; -import { PartialPackageData } from "./types.js"; - -export async function readPackageData() { - return ( - (JSON.parse(await readFileSafe("./package.json", "{}")) as - | PartialPackageData - | undefined) ?? {} - ); -} - -export async function removeDependencies( - packageNames: string[], - existing: Record = {}, - flags = "", -) { - const present = packageNames.filter((packageName) => packageName in existing); - - if (present.length) { - await execaCommand( - `pnpm remove ${present.join(" ")}${flags ? ` ${flags}` : ""}`, - ); - } -} diff --git a/src/shared/prompts.ts b/src/shared/prompts.ts deleted file mode 100644 index 4202b6677..000000000 --- a/src/shared/prompts.ts +++ /dev/null @@ -1,5 +0,0 @@ -import * as prompts from "@clack/prompts"; - -export function filterPromptCancel(value: symbol | Value) { - return prompts.isCancel(value) ? undefined : value; -} diff --git a/src/shared/readFileSafeAsJson.ts b/src/shared/readFileSafeAsJson.ts deleted file mode 100644 index af2dcbe3c..000000000 --- a/src/shared/readFileSafeAsJson.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { readFileSafe } from "./readFileSafe.js"; - -export async function readFileSafeAsJson(filePath: string | URL) { - return JSON.parse(await readFileSafe(filePath, "null")) as unknown; -} diff --git a/src/shared/runOrRestore.test.ts b/src/shared/runOrRestore.test.ts deleted file mode 100644 index b7f9edc9b..000000000 --- a/src/shared/runOrRestore.test.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; - -import { runOrRestore } from "./runOrRestore.js"; - -const mockConfirm = vi.fn(); - -vi.mock("@clack/prompts", () => ({ - get confirm() { - return mockConfirm; - }, - intro: vi.fn(), - isCancel: vi.fn(), - outro: vi.fn(), -})); - -const mock$ = vi.fn(); - -vi.mock("execa", () => ({ - get $() { - return mock$; - }, -})); - -const mockGetInputValuesAndOctokit = vi.fn(); - -vi.mock("./inputs.js", () => ({ - get getInputValuesAndOctokit() { - return mockGetInputValuesAndOctokit; - }, -})); - -describe("runOrRestore", () => { - beforeEach(() => { - vi.spyOn(console, "clear").mockImplementation(() => undefined); - vi.spyOn(console, "log").mockImplementation(() => undefined); - }); - - it("returns 0 when run resolves", async () => { - mockGetInputValuesAndOctokit.mockResolvedValue({ - octokit: undefined, - values: {}, - }); - - const actual = await runOrRestore({ - run: vi.fn(), - skipRestore: true, - }); - - expect(actual).toBe(0); - }); - - it("returns 2 and does not restore the repository when run rejects and skipRestore is true", async () => { - mockGetInputValuesAndOctokit.mockResolvedValue({ - octokit: undefined, - values: { - skipRestore: false, - }, - }); - mockConfirm.mockResolvedValue(false); - - const actual = await runOrRestore({ - run: vi.fn().mockRejectedValue(new Error("Oh no!")), - skipRestore: true, - }); - - expect(actual).toBe(2); - expect(mock$).toHaveBeenCalledTimes(0); - }); - - it("returns 2 and restores the repository when run rejects and skipRestore is false", async () => { - mockGetInputValuesAndOctokit.mockResolvedValue({ - octokit: undefined, - values: { - skipRestore: false, - }, - }); - mockConfirm.mockResolvedValue(true); - - const actual = await runOrRestore({ - run: vi.fn().mockRejectedValue(new Error("Oh no!")), - skipRestore: false, - }); - - expect(actual).toBe(2); - expect(mock$.mock.calls).toMatchInlineSnapshot(` - [ - [ - [ - "git restore .", - ], - ], - ] - `); - }); -}); diff --git a/src/shared/runOrRestore.ts b/src/shared/runOrRestore.ts deleted file mode 100644 index 4d48f3157..000000000 --- a/src/shared/runOrRestore.ts +++ /dev/null @@ -1,43 +0,0 @@ -import * as prompts from "@clack/prompts"; -import chalk from "chalk"; -import { $ } from "execa"; - -import { logLine } from "./cli/lines.js"; -import { StatusCodes } from "./codes.js"; - -export interface RunOrRestoreOptions { - run: () => Promise; - skipRestore: boolean | undefined; -} - -export async function runOrRestore({ run, skipRestore }: RunOrRestoreOptions) { - try { - await run(); - return StatusCodes.Success; - } catch (error) { - logLine(); - console.log(error); - - if (skipRestore) { - logLine(); - logLine(chalk.gray`Leaving changes to the local directory on disk.`); - } else { - const shouldRestore = await prompts.confirm({ - message: "Do you want to restore the repository to how it was?", - }); - - if (shouldRestore) { - logLine(); - logLine( - [ - chalk.gray`Resetting repository using`, - chalk.reset`git restore .`, - ].join(" "), - ); - await $`git restore .`; - } - } - - return 2; - } -} diff --git a/src/shared/types.ts b/src/shared/types.ts deleted file mode 100644 index 2ea042f38..000000000 --- a/src/shared/types.ts +++ /dev/null @@ -1,127 +0,0 @@ -import { z } from "zod"; - -import { StatusCode } from "./codes.js"; - -export interface AllContributorContributor { - avatar_url: string; - contributions: string[]; - login: string; - name: string; - profile: string; -} - -export interface AllContributorsData { - contributors: AllContributorContributor[]; -} - -export type OptionsAccess = "public" | "restricted"; - -export type OptionsBase = "common" | "everything" | "minimal" | "prompt"; - -export interface OptionsEmail { - github: string; - npm: string; -} - -export interface OptionsGuide { - href: string; - title: string; -} - -export interface OptionsLogo { - alt: string; - height?: number; - src: string; - width?: number; -} - -export interface PartialPackageData { - author?: string | { email: string; name: string }; - bin?: string; - dependencies?: Record; - description?: string; - devDependencies?: Record; - email?: string; - engines?: { node?: string }; - name?: string; - repository?: string | { type: string; url: string }; - scripts?: Record; - version?: string; -} - -/** - * All runtime options that may (or must) be specified for setup. - */ -export interface Options { - access: OptionsAccess; - author?: string; - base?: OptionsBase; - bin?: string; - description: string; - directory: string; - email: OptionsEmail; - excludeAllContributors?: boolean; - excludeBuild?: boolean; - excludeCompliance?: boolean; - excludeLintESLint?: boolean; - excludeLintJSDoc?: boolean; - excludeLintJson?: boolean; - excludeLintKnip?: boolean; - excludeLintMd?: boolean; - excludeLintPackageJson?: boolean; - excludeLintPackages?: boolean; - excludeLintPerfectionist?: boolean; - excludeLintRegexp?: boolean; - excludeLintSpelling?: boolean; - excludeLintStrict?: boolean; - excludeLintStylistic?: boolean; - excludeLintYml?: boolean; - excludeReleases?: boolean; - excludeRenovate?: boolean; - excludeTemplatedBy?: boolean; - excludeTests?: boolean; - funding?: string; - guide?: OptionsGuide; - keywords?: string[]; - logo?: OptionsLogo; - mode: Mode; - offline?: boolean; - owner: string; - preserveGeneratedFrom?: boolean; - repository: string; - skipAllContributorsApi?: boolean; - skipGitHubApi?: boolean; - skipInstall?: boolean; - skipRemoval?: boolean; - skipRestore?: boolean; - skipUninstall?: boolean; - title: string; -} - -/** - * Options that might be suggested by how the user is running setup. - */ -export type Mode = "create" | "initialize" | "migrate"; - -export interface ModeResult { - code: StatusCode; - error?: string | z.ZodError; - options: Partial; -} - -export type ModeRunner = ( - args: string[], - promptedOptions?: PromptedOptions, -) => Promise; - -export interface PromptedOptions { - /** - * Directory for the repository, if it may differ from the repository name. - */ - directory?: string; - - /** - * Repository name, if it may differ from the current directory. - */ - repository?: string; -} diff --git a/src/steps/addOwnerAsAllContributor.test.ts b/src/steps/addOwnerAsAllContributor.test.ts deleted file mode 100644 index ec2d23be8..000000000 --- a/src/steps/addOwnerAsAllContributor.test.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; - -import { addOwnerAsAllContributor } from "./addOwnerAsAllContributor.js"; -import { formatJson } from "./writing/creation/formatters/formatJson.js"; - -const mock$ = vi.fn(); - -vi.mock("execa", () => ({ - get $() { - return mock$; - }, -})); - -const mockWriteFile = vi.fn(); - -vi.mock("node:fs/promises", () => ({ - get writeFile() { - return mockWriteFile; - }, -})); - -const mockReadFileAsJson = vi.fn(); - -vi.mock("../shared/readFileAsJson.js", () => ({ - get readFileAsJson() { - return mockReadFileAsJson; - }, -})); - -const mockOwner = "TestOwner"; - -vi.mock("../shared/getGitHubUserAsAllContributor", () => ({ - getGitHubUserAsAllContributor: () => mockOwner, -})); - -describe("addOwnerAsAllContributor", () => { - it("throws an error when the .all-contributorsrc fails to read", async () => { - mock$.mockResolvedValueOnce({ - stdout: JSON.stringify({ login: "user" }), - }); - mockReadFileAsJson.mockResolvedValue("invalid"); - - await expect(async () => { - await addOwnerAsAllContributor(undefined, { owner: mockOwner }); - }).rejects.toMatchInlineSnapshot( - '[Error: Invalid .all-contributorsrc: "invalid"]', - ); - }); - - it("throws an error when the .all-contributorsrc is missing expected properties", async () => { - mock$.mockResolvedValueOnce({ - stdout: JSON.stringify({ login: "user" }), - }); - mockReadFileAsJson.mockResolvedValue({}); - - await expect(async () => { - await addOwnerAsAllContributor(undefined, { owner: mockOwner }); - }).rejects.toMatchInlineSnapshot( - "[Error: Invalid .all-contributorsrc: {}]", - ); - }); - - it("sets the running user to tool when no prior contributions exist", async () => { - mock$.mockResolvedValueOnce({ - stdout: JSON.stringify({ login: mockOwner }), - }); - mockReadFileAsJson.mockResolvedValue({ - contributors: [], - }); - - await addOwnerAsAllContributor(undefined, { owner: mockOwner }); - - expect(mockWriteFile).toHaveBeenCalledWith( - "./.all-contributorsrc", - await formatJson({ - contributors: [{ contributions: ["tool"], login: mockOwner }], - }), - ); - }); - - it("resets JoshuaKGoldberg to just tool and adds in the running user when both exist", async () => { - mock$.mockResolvedValueOnce({ - stdout: JSON.stringify({ login: mockOwner }), - }); - mockReadFileAsJson.mockResolvedValue({ - contributors: [ - { contributions: ["bug", "fix"], login: mockOwner }, - { contributions: ["bug", "fix"], login: "JoshuaKGoldberg" }, - ], - }); - - await addOwnerAsAllContributor(undefined, { owner: mockOwner }); - - expect(mockWriteFile).toHaveBeenCalledWith( - "./.all-contributorsrc", - await formatJson({ - contributors: [ - { contributions: ["bug", "fix", "tool"], login: mockOwner }, - { contributions: ["tool"], login: "JoshuaKGoldberg" }, - ], - }), - ); - }); -}); diff --git a/src/steps/addOwnerAsAllContributor.ts b/src/steps/addOwnerAsAllContributor.ts deleted file mode 100644 index 8a6e420f6..000000000 --- a/src/steps/addOwnerAsAllContributor.ts +++ /dev/null @@ -1,61 +0,0 @@ -import * as fs from "node:fs/promises"; -import { Octokit } from "octokit"; - -import { getGitHubUserAsAllContributor } from "../shared/getGitHubUserAsAllContributor.js"; -import { readFileAsJson } from "../shared/readFileAsJson.js"; -import { - AllContributorContributor, - AllContributorsData, - Options, -} from "../shared/types.js"; -import { formatJson } from "./writing/creation/formatters/formatJson.js"; - -export async function addOwnerAsAllContributor( - octokit: Octokit | undefined, - options: Pick, -) { - const user = await getGitHubUserAsAllContributor(octokit, options); - - const existingContributors = (await readFileAsJson( - "./.all-contributorsrc", - )) as AllContributorsData; - if (!isValidAllContributorsData(existingContributors)) { - throw new Error( - `Invalid .all-contributorsrc: ${JSON.stringify(existingContributors)}`, - ); - } - - const contributors = existingContributors.contributors - .filter(({ login }) => ["JoshuaKGoldberg", user].includes(login)) - .map((contributor) => - contributor.login === "JoshuaKGoldberg" - ? { ...contributor, contributions: ["tool"] } - : { - ...contributor, - contributions: Array.from( - new Set(["tool", ...contributor.contributions]), - ).sort(), - }, - ); - - if (!contributors.some((contributor) => contributor.login === user)) { - contributors.push({ - contributions: ["tool"], - login: user, - } as AllContributorContributor); - } - - await fs.writeFile( - "./.all-contributorsrc", - await formatJson({ - ...existingContributors, - contributors, - }), - ); -} - -function isValidAllContributorsData( - value: unknown, -): value is AllContributorsData { - return !!value && typeof value === "object" && "contributors" in value; -} diff --git a/src/steps/addToolAllContributors.test.ts b/src/steps/addToolAllContributors.test.ts deleted file mode 100644 index 44684d902..000000000 --- a/src/steps/addToolAllContributors.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; - -import { addToolAllContributors } from "./addToolAllContributors.js"; - -const mock$ = vi.fn(); - -vi.mock("execa", () => ({ - get $() { - return mock$; - }, -})); - -const mockGetGitHubUserAsAllContributor = vi.fn(); - -vi.mock("../shared/getGitHubUserAsAllContributor.js", () => ({ - get getGitHubUserAsAllContributor() { - return mockGetGitHubUserAsAllContributor; - }, -})); - -describe("addToolAllContributors", () => { - it("adds JoshuaKGoldberg when that is not the current github user", async () => { - mockGetGitHubUserAsAllContributor.mockResolvedValue("JoshuaKGoldberg"); - - await addToolAllContributors(undefined, { owner: "owner" }); - - expect(mock$).not.toHaveBeenCalled(); - }); - - it("does not add JoshuaKGoldberg when that not the current github user", async () => { - mockGetGitHubUserAsAllContributor.mockResolvedValue("other"); - - await addToolAllContributors(undefined, { owner: "owner" }); - - expect(mock$).toHaveBeenCalledWith([ - `pnpx all-contributors-cli add JoshuaKGoldberg tool`, - ]); - }); -}); diff --git a/src/steps/addToolAllContributors.ts b/src/steps/addToolAllContributors.ts deleted file mode 100644 index 2fa6cf571..000000000 --- a/src/steps/addToolAllContributors.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { $ } from "execa"; -import { Octokit } from "octokit"; - -import { getGitHubUserAsAllContributor } from "../shared/getGitHubUserAsAllContributor.js"; -import { Options } from "../shared/types.js"; - -export async function addToolAllContributors( - octokit: Octokit | undefined, - options: Pick, -) { - const login = await getGitHubUserAsAllContributor(octokit, options); - - if (login !== "JoshuaKGoldberg") { - await $`pnpx all-contributors-cli add JoshuaKGoldberg tool`; - } -} diff --git a/src/steps/clearChangelog.ts b/src/steps/clearChangelog.ts deleted file mode 100644 index f2641cf12..000000000 --- a/src/steps/clearChangelog.ts +++ /dev/null @@ -1,9 +0,0 @@ -import * as fs from "node:fs/promises"; -import prettier from "prettier"; - -export async function clearChangelog() { - await fs.writeFile( - "./CHANGELOG.md", - await prettier.format(`# Changelog`, { parser: "markdown" }), - ); -} diff --git a/src/steps/clearLocalGitTags.ts b/src/steps/clearLocalGitTags.ts deleted file mode 100644 index c7c9d00fa..000000000 --- a/src/steps/clearLocalGitTags.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { execaCommand } from "execa"; - -export async function clearLocalGitTags() { - const tags = await execaCommand("git tag -l"); - - await execaCommand(`git tag -d ${tags.stdout.replaceAll("\n", " ")}`); -} diff --git a/src/steps/clearUnnecessaryFiles.ts b/src/steps/clearUnnecessaryFiles.ts deleted file mode 100644 index 85f0e4b3a..000000000 --- a/src/steps/clearUnnecessaryFiles.ts +++ /dev/null @@ -1,37 +0,0 @@ -import * as fs from "node:fs/promises"; - -const globPaths = [ - ...extensions(".babelrc", "cjs", "cts", "js", "json", "mjs"), - ...extensions(".eslintrc", "js", "json", "yml"), - ...extensions(".mocha", "cjs", "js", "json", "jsonc", "yaml", "yml"), - ...extensions(".prettierrc", "json", "json5", "yaml", "yml"), - ...extensions("prettier.config", "js", "mjs", "cjs"), - ...extensions("babel.config", "cjs", "cts", "js", "json", "mjs"), - ...extensions("jest.config", "cjs", "js", "json", "mjs", "ts"), - "./src/**/*.js", - ".circleci/config.yml", - ".github/codecov.yml", - ".babelrc", - ".npmignore", - ".eslintignore", - ".eslintrc", - "CODE_OF_CONDUCT.md", - "CONTRIBUTING.md", - "codecov.yml", - "DEVELOPMENT.md", - "dist", - "lib", - "package-lock.json", - "travis.yml", - "yarn.lock", -]; - -export async function clearUnnecessaryFiles() { - for (const globPath of globPaths) { - await fs.rm(globPath, { force: true, recursive: true }); - } -} - -function extensions(base: string, ...extensions: string[]) { - return extensions.map((extension) => [base, extension].join(".")); -} diff --git a/src/steps/createJoshuaKGoldbergReplacement.test.ts b/src/steps/createJoshuaKGoldbergReplacement.test.ts deleted file mode 100644 index 14bb7c00d..000000000 --- a/src/steps/createJoshuaKGoldbergReplacement.test.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { describe, expect, test } from "vitest"; - -import { createJoshuaKGoldbergReplacement } from "./createJoshuaKGoldbergReplacement.js"; - -const options = { - owner: "NewOwner", - repository: "new-repository", -}; - -describe("createJoshuaKGoldbergReplacement", () => { - test.each([ - [`JoshuaKGoldberg`, options.owner], - [ - `JoshuaKGoldberg/${options.repository}`, - `${options.owner}/${options.repository}`, - ], - [`JoshuaKGoldberg/other-repository`, `JoshuaKGoldberg/other-repository`], - ])("%s", (before, expected) => { - const [matcher, replacer] = createJoshuaKGoldbergReplacement(options); - - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const actual = replacer(before, matcher.exec(before)![1]); - - expect(actual).toBe(expected); - }); -}); diff --git a/src/steps/createJoshuaKGoldbergReplacement.ts b/src/steps/createJoshuaKGoldbergReplacement.ts deleted file mode 100644 index d33f2b9ec..000000000 --- a/src/steps/createJoshuaKGoldbergReplacement.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Options } from "../shared/types.js"; - -/** - * Creates a replace-in-file replacement for JoshuaKGoldberg/... matches, - * keeping repository names not being migrated (e.g. for GitHub actions). - */ -export const createJoshuaKGoldbergReplacement = ( - options: Pick, -): [RegExp, ToCallback] => [ - /JoshuaKGoldberg(?:\/(.+))?/g, - (full: string, capture: string | undefined) => - capture - ? // If this was a "JoshuaKGoldberg/..." repository link, - // swap the owner if it's the repository being migrated. - capture.startsWith(options.repository) - ? `${options.owner}/${capture}` - : full - : // Otherwise it's just "JoshuaKGoldberg" standalone, - // so swap to the new owner. - options.owner, -]; diff --git a/src/steps/finalizeDependencies.test.ts b/src/steps/finalizeDependencies.test.ts deleted file mode 100644 index 8025417a4..000000000 --- a/src/steps/finalizeDependencies.test.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; - -import { Options } from "../shared/types.js"; -import { finalizeDependencies } from "./finalizeDependencies.js"; - -const mockExecaCommand = vi.fn(); - -vi.mock("execa", () => ({ - get execaCommand() { - return mockExecaCommand; - }, -})); - -vi.mock("../shared/packages.js", () => ({ - readPackageData: () => [], - removeDependencies: vi.fn(), -})); - -const options = { - access: "public", - base: "everything", - description: "Stub description.", - directory: ".", - email: { - github: "github@email.com", - npm: "npm@email.com", - }, - mode: "create", - owner: "StubOwner", - repository: "stub-repository", - title: "Stub Title", -} satisfies Options; - -describe("finalize", () => { - it("installs the full list of commands when no options are enabled", async () => { - await finalizeDependencies(options); - - expect(mockExecaCommand.mock.calls).toMatchInlineSnapshot(` - [ - [ - "pnpm add @eslint-community/eslint-plugin-eslint-comments@latest @eslint/js@latest @release-it/conventional-changelog@latest @types/eslint-plugin-markdown@latest @types/node@latest @vitest/coverage-v8@latest @vitest/eslint-plugin@latest all-contributors-cli@latest console-fail-test@latest cspell@latest eslint@latest eslint-plugin-jsdoc@latest eslint-plugin-jsonc@latest eslint-plugin-markdown@latest eslint-plugin-n@latest eslint-plugin-package-json@latest eslint-plugin-perfectionist@latest eslint-plugin-regexp@latest eslint-plugin-yml@latest husky@latest knip@latest lint-staged@latest markdownlint@latest markdownlint-cli@latest prettier@latest prettier-plugin-curly@latest prettier-plugin-packagejson@latest prettier-plugin-sh@latest release-it@latest sentences-per-line@latest tsup@latest typescript@latest typescript-eslint@latest vitest@latest -D", - ], - [ - "npx all-contributors-cli generate", - ], - [ - "pnpm dedupe --offline", - ], - ] - `); - }); - - it("installs in offline mode when options.offline is true", async () => { - await finalizeDependencies({ - ...options, - offline: true, - }); - - expect(mockExecaCommand.mock.calls).toMatchInlineSnapshot(` - [ - [ - "pnpm add @eslint-community/eslint-plugin-eslint-comments@latest @eslint/js@latest @release-it/conventional-changelog@latest @types/eslint-plugin-markdown@latest @types/node@latest @vitest/coverage-v8@latest @vitest/eslint-plugin@latest all-contributors-cli@latest console-fail-test@latest cspell@latest eslint@latest eslint-plugin-jsdoc@latest eslint-plugin-jsonc@latest eslint-plugin-markdown@latest eslint-plugin-n@latest eslint-plugin-package-json@latest eslint-plugin-perfectionist@latest eslint-plugin-regexp@latest eslint-plugin-yml@latest husky@latest knip@latest lint-staged@latest markdownlint@latest markdownlint-cli@latest prettier@latest prettier-plugin-curly@latest prettier-plugin-packagejson@latest prettier-plugin-sh@latest release-it@latest sentences-per-line@latest tsup@latest typescript@latest typescript-eslint@latest vitest@latest -D --offline", - ], - [ - "npx all-contributors-cli generate", - ], - [ - "pnpm dedupe --offline", - ], - ] - `); - }); - - it("installs the base list of commands when all options are enabled", async () => { - await finalizeDependencies({ - ...options, - excludeAllContributors: true, - excludeCompliance: true, - excludeLintJson: true, - excludeLintKnip: true, - excludeLintMd: true, - excludeLintPackageJson: true, - excludeLintPackages: true, - excludeLintPerfectionist: true, - excludeLintSpelling: true, - excludeLintYml: true, - excludeReleases: true, - excludeRenovate: undefined, - excludeTests: true, - }); - - expect(mockExecaCommand.mock.calls).toMatchInlineSnapshot(` - [ - [ - "pnpm add @eslint-community/eslint-plugin-eslint-comments@latest @eslint/js@latest @types/node@latest eslint@latest eslint-plugin-jsdoc@latest eslint-plugin-n@latest eslint-plugin-regexp@latest husky@latest lint-staged@latest prettier@latest prettier-plugin-curly@latest prettier-plugin-packagejson@latest prettier-plugin-sh@latest tsup@latest typescript@latest typescript-eslint@latest -D", - ], - [ - "pnpm dedupe --offline", - ], - ] - `); - }); -}); diff --git a/src/steps/finalizeDependencies.ts b/src/steps/finalizeDependencies.ts deleted file mode 100644 index de8b9936f..000000000 --- a/src/steps/finalizeDependencies.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { execaCommand } from "execa"; - -import { readPackageData, removeDependencies } from "../shared/packages.js"; -import { Options } from "../shared/types.js"; - -export async function finalizeDependencies(options: Options) { - const devDependencies = [ - "@eslint/js", - "@types/node", - "eslint-plugin-n", - "eslint", - "husky", - "lint-staged", - "prettier-plugin-curly", - "prettier-plugin-packagejson", - "prettier-plugin-sh", - "prettier", - "tsup", - "typescript-eslint", - "typescript", - ...(options.excludeAllContributors ? [] : ["all-contributors-cli"]), - ...(options.excludeLintESLint - ? [] - : ["@eslint-community/eslint-plugin-eslint-comments"]), - ...(options.excludeLintJSDoc ? [] : ["eslint-plugin-jsdoc"]), - ...(options.excludeLintJson ? [] : ["eslint-plugin-jsonc"]), - ...(options.excludeLintKnip ? [] : ["knip"]), - ...(options.excludeLintMd - ? [] - : [ - "@types/eslint-plugin-markdown", - "eslint-plugin-markdown", - "markdownlint", - "markdownlint-cli", - "sentences-per-line", - ]), - ...(options.excludeLintPackageJson ? [] : ["eslint-plugin-package-json"]), - ...(options.excludeLintPerfectionist - ? [] - : ["eslint-plugin-perfectionist"]), - ...(options.excludeLintRegexp ? [] : ["eslint-plugin-regexp"]), - ...(options.excludeLintSpelling ? [] : ["cspell"]), - ...(options.excludeLintYml ? [] : ["eslint-plugin-yml"]), - ...(options.excludeReleases - ? [] - : ["@release-it/conventional-changelog", "release-it"]), - ...(options.excludeTests - ? [] - : [ - "@vitest/coverage-v8", - "@vitest/eslint-plugin", - "console-fail-test", - "vitest", - ]), - ] - .filter(Boolean) - .sort() - .map((packageName) => `${packageName}@latest`) - .join(" "); - - await execaCommand( - `pnpm add ${devDependencies} -D${options.offline ? " --offline" : ""}`, - ); - - if (!options.excludeAllContributors) { - await execaCommand(`npx all-contributors-cli generate`); - await removeDependencies( - ["all-contributors-cli"], - (await readPackageData()).devDependencies, - "-D", - ); - } - - await execaCommand(`pnpm dedupe --offline`); -} diff --git a/src/steps/initializeBranchProtectionSettings.test.ts b/src/steps/initializeBranchProtectionSettings.test.ts deleted file mode 100644 index 0c915d3c6..000000000 --- a/src/steps/initializeBranchProtectionSettings.test.ts +++ /dev/null @@ -1,222 +0,0 @@ -import { Octokit } from "octokit"; -import { describe, expect, it, MockInstance, vi } from "vitest"; - -import { Options } from "../shared/types.js"; -import { initializeBranchProtectionSettings } from "./initializeGitHubRepository/initializeBranchProtectionSettings.js"; - -const createMockOctokit = (request: MockInstance) => - ({ - request, - }) as unknown as Octokit; - -const stubOptions = { - access: "public", - description: "", - directory: "", - email: { - github: "", - npm: "", - }, - mode: "create", - owner: "", - repository: "", - title: "", -} satisfies Options; - -describe("migrateBranchProtectionSettings", () => { - it("does not throw when the request receives a non-error response", async () => { - const mockRequest = vi.fn().mockResolvedValue({ status: 200 }); - - await expect( - initializeBranchProtectionSettings( - createMockOctokit(mockRequest), - stubOptions, - ), - ).resolves.not.toThrow(); - - expect(mockRequest.mock.calls).toMatchInlineSnapshot(` - [ - [ - "POST /repos/{owner}/{repo}/rulesets", - { - "bypass_actors": [ - { - "actor_id": 5, - "actor_type": "RepositoryRole", - "bypass_mode": "always", - }, - ], - "conditions": { - "ref_name": { - "exclude": [], - "include": [ - "refs/heads/main", - ], - }, - }, - "enforcement": "active", - "name": "Branch protection for main", - "owner": "", - "repo": "", - "rules": [ - { - "type": "deletion", - }, - { - "parameters": { - "allowed_merge_methods": [ - "squash", - ], - "dismiss_stale_reviews_on_push": false, - "require_code_owner_review": false, - "require_last_push_approval": false, - "required_approving_review_count": 0, - "required_review_thread_resolution": false, - }, - "type": "pull_request", - }, - { - "parameters": { - "required_status_checks": [ - { - "context": "Build", - }, - { - "context": "Compliance", - }, - { - "context": "Lint", - }, - { - "context": "Lint Knip", - }, - { - "context": "Lint Markdown", - }, - { - "context": "Lint Packages", - }, - { - "context": "Lint Spelling", - }, - { - "context": "Prettier", - }, - { - "context": "Test", - }, - ], - "strict_required_status_checks_policy": false, - }, - "type": "required_status_checks", - }, - ], - "target": "branch", - }, - ], - ] - `); - }); - - it("returns false when the request receives a 403 response", async () => { - const mockRequest = vi.fn().mockRejectedValue({ status: 403 }); - - const actual = await initializeBranchProtectionSettings( - createMockOctokit(mockRequest), - stubOptions, - ); - - expect(actual).toBe(false); - }); - - it("throws the error when the request throws with a non-403 response", async () => { - const error = { status: 404 }; - const mockRequest = vi.fn().mockRejectedValue(error); - - await expect(() => - initializeBranchProtectionSettings( - createMockOctokit(mockRequest), - stubOptions, - ), - ).rejects.toBe(error); - }); - - it("doesn't create workflows for excluded options when specified", async () => { - const mockRequest = vi.fn().mockResolvedValue({ status: 200 }); - - await initializeBranchProtectionSettings(createMockOctokit(mockRequest), { - ...stubOptions, - excludeCompliance: true, - excludeLintKnip: true, - excludeLintMd: true, - excludeLintPackages: true, - excludeLintSpelling: true, - excludeTests: true, - }); - - expect(mockRequest.mock.calls).toMatchInlineSnapshot(` - [ - [ - "POST /repos/{owner}/{repo}/rulesets", - { - "bypass_actors": [ - { - "actor_id": 5, - "actor_type": "RepositoryRole", - "bypass_mode": "always", - }, - ], - "conditions": { - "ref_name": { - "exclude": [], - "include": [ - "refs/heads/main", - ], - }, - }, - "enforcement": "active", - "name": "Branch protection for main", - "owner": "", - "repo": "", - "rules": [ - { - "type": "deletion", - }, - { - "parameters": { - "allowed_merge_methods": [ - "squash", - ], - "dismiss_stale_reviews_on_push": false, - "require_code_owner_review": false, - "require_last_push_approval": false, - "required_approving_review_count": 0, - "required_review_thread_resolution": false, - }, - "type": "pull_request", - }, - { - "parameters": { - "required_status_checks": [ - { - "context": "Build", - }, - { - "context": "Lint", - }, - { - "context": "Prettier", - }, - ], - "strict_required_status_checks_policy": false, - }, - "type": "required_status_checks", - }, - ], - "target": "branch", - }, - ], - ] - `); - }); -}); diff --git a/src/steps/initializeGitHubRepository/index.ts b/src/steps/initializeGitHubRepository/index.ts deleted file mode 100644 index c266a4745..000000000 --- a/src/steps/initializeGitHubRepository/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Octokit } from "octokit"; -import { setGitHubRepositoryLabels } from "set-github-repository-labels"; - -import { Options } from "../../shared/types.js"; -import { initializeGitRemote } from "../initializeGitRemote.js"; -import { initializeRepositorySettings } from "../initializeRepositorySettings.js"; -import { initializeBranchProtectionSettings } from "./initializeBranchProtectionSettings.js"; -import { outcomeLabels } from "./outcomeLabels.js"; - -export async function initializeGitHubRepository( - octokit: Octokit, - options: Options, -) { - await initializeGitRemote(options); - await initializeRepositorySettings(octokit, options); - await initializeBranchProtectionSettings(octokit, options); - await setGitHubRepositoryLabels({ - labels: outcomeLabels, - owner: options.owner, - repository: options.repository, - }); -} diff --git a/src/steps/initializeGitHubRepository/initializeBranchProtectionSettings.ts b/src/steps/initializeGitHubRepository/initializeBranchProtectionSettings.ts deleted file mode 100644 index 7a9de96ea..000000000 --- a/src/steps/initializeGitHubRepository/initializeBranchProtectionSettings.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { RequestError } from "@octokit/request-error"; -import { Octokit } from "octokit"; - -import { Options } from "../../shared/types.js"; - -export async function initializeBranchProtectionSettings( - octokit: Octokit, - options: Options, -) { - try { - await octokit.request("POST /repos/{owner}/{repo}/rulesets", { - bypass_actors: [ - { - // This *seems* to be the Repository Admin role always? - // https://github.com/github/rest-api-description/issues/4406 - actor_id: 5, - actor_type: "RepositoryRole", - bypass_mode: "always", - }, - ], - conditions: { - ref_name: { - exclude: [], - include: ["refs/heads/main"], - }, - }, - enforcement: "active", - name: "Branch protection for main", - owner: options.owner, - repo: options.repository, - rules: [ - { type: "deletion" }, - { - parameters: { - // @ts-expect-error -- https://github.com/github/rest-api-description/issues/4405 - allowed_merge_methods: ["squash"], - dismiss_stale_reviews_on_push: false, - require_code_owner_review: false, - require_last_push_approval: false, - required_approving_review_count: 0, - required_review_thread_resolution: false, - }, - type: "pull_request", - }, - { - parameters: { - required_status_checks: [ - { context: "Build" }, - ...(options.excludeCompliance ? [] : [{ context: "Compliance" }]), - { context: "Lint" }, - ...(options.excludeLintKnip ? [] : [{ context: "Lint Knip" }]), - ...(options.excludeLintMd ? [] : [{ context: "Lint Markdown" }]), - ...(options.excludeLintPackages - ? [] - : [{ context: "Lint Packages" }]), - ...(options.excludeLintSpelling - ? [] - : [{ context: "Lint Spelling" }]), - { context: "Prettier" }, - ...(options.excludeTests ? [] : [{ context: "Test" }]), - ], - strict_required_status_checks_policy: false, - }, - type: "required_status_checks", - }, - ], - target: "branch", - }); - } catch (error) { - if ((error as RequestError).status === 403) { - return false; - } - - throw error; - } -} diff --git a/src/steps/initializeGitRemote.test.ts b/src/steps/initializeGitRemote.test.ts deleted file mode 100644 index 65b67d95b..000000000 --- a/src/steps/initializeGitRemote.test.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; - -import { initializeGitRemote } from "./initializeGitRemote.js"; - -const mock$ = vi.fn(); - -vi.mock("execa", () => ({ - get $() { - return mock$; - }, -})); - -const options = { - owner: "TestOwner", - repository: "test-repository", -}; - -describe("initializeGitRemote", () => { - it("does not add an origin or fetch when remotes already includes origin", async () => { - mock$.mockResolvedValue({ - stdout: "origin", - }); - - await initializeGitRemote(options); - - expect(mock$.mock.calls).toMatchInlineSnapshot(` - [ - [ - [ - "git remote", - ], - ], - ] - `); - }); - - it("add an origin and fetch when remotes does not include origin", async () => { - mock$.mockResolvedValue({ - stdout: "", - }); - - await initializeGitRemote(options); - - expect(mock$.mock.calls).toMatchInlineSnapshot(` - [ - [ - [ - "git remote", - ], - ], - [ - [ - "git remote add origin https://github.com/", - "/", - "", - ], - "TestOwner", - "test-repository", - ], - [ - [ - "git fetch", - ], - ], - ] - `); - }); -}); diff --git a/src/steps/initializeGitRemote.ts b/src/steps/initializeGitRemote.ts deleted file mode 100644 index 46c7a70a7..000000000 --- a/src/steps/initializeGitRemote.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { $ } from "execa"; - -import { Options } from "../shared/types.js"; - -type InitializeRepositorySettings = Pick; - -export async function initializeGitRemote({ - owner, - repository, -}: InitializeRepositorySettings) { - const remotes = (await $`git remote`).stdout.trim().split("\n"); - - if (!remotes.includes("origin")) { - await $`git remote add origin https://github.com/${owner}/${repository}`; - await $`git fetch`; - } -} diff --git a/src/steps/initializeRepositorySettings.ts b/src/steps/initializeRepositorySettings.ts deleted file mode 100644 index 063648eb6..000000000 --- a/src/steps/initializeRepositorySettings.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Octokit } from "octokit"; - -import { Options } from "../shared/types.js"; - -type InitializeRepositorySettings = Pick< - Options, - "description" | "owner" | "repository" ->; - -export async function initializeRepositorySettings( - octokit: Octokit, - { description, owner, repository }: InitializeRepositorySettings, -) { - await octokit.rest.repos.update({ - allow_auto_merge: true, - allow_rebase_merge: false, - allow_squash_merge: true, - default_branch: "main", - delete_branch_on_merge: true, - description, - has_wiki: false, - owner, - repo: repository, - security_and_analysis: { - secret_scanning: { - status: "enabled", - }, - secret_scanning_push_protection: { - status: "enabled", - }, - }, - squash_merge_commit_message: "PR_BODY", - squash_merge_commit_title: "PR_TITLE", - }); -} diff --git a/src/steps/populateCSpellDictionary.test.ts b/src/steps/populateCSpellDictionary.test.ts deleted file mode 100644 index 0d50a6aa9..000000000 --- a/src/steps/populateCSpellDictionary.test.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; - -import { populateCSpellDictionary } from "./populateCSpellDictionary.js"; - -const mock$ = vi.fn(); - -vi.mock("execa", () => ({ - get $() { - return mock$; - }, -})); - -const mockReadFile = vi.fn(); -const mockWriteFile = vi.fn(); - -vi.mock("node:fs/promises", () => ({ - get readFile() { - return mockReadFile; - }, - get writeFile() { - return mockWriteFile; - }, -})); - -const mockFormatJson = vi.fn(); - -vi.mock("./writing/creation/formatters/formatJson.js", () => ({ - get formatJson() { - return mockFormatJson; - }, -})); - -describe("populateCSpellDictionary", () => { - it("works with no existing words when the existing cspell.json has no words", async () => { - const unknownWords = ["abc"]; - - mock$.mockResolvedValue({ - stdout: ` - file-1.ts Unknown word (${unknownWords[0]}) - `, - }); - - mockReadFile.mockResolvedValue(JSON.stringify({})); - - await populateCSpellDictionary(); - - expect(mockFormatJson.mock.calls).toMatchInlineSnapshot(` - [ - [ - { - "words": [ - "abc", - ], - }, - ], - ] - `); - }); - - it("adds unknown words to cspell.json", async () => { - const existingWords = ["abc", "ghi", "casing"]; - const unknownWords = ["def", "jkl", "Casing"]; - - mock$.mockResolvedValue({ - stdout: ` - file-1.ts Unknown word (${unknownWords[0]}) - file-2.ts Unknown word (${unknownWords[1]}) - file-2.ts Unknown word (${unknownWords[0]}) - `, - }); - - mockReadFile.mockResolvedValue(JSON.stringify({ words: existingWords })); - - await populateCSpellDictionary(); - - expect(mockFormatJson.mock.calls).toMatchInlineSnapshot(` - [ - [ - { - "words": [ - "abc", - "casing", - "def", - "ghi", - "jkl", - ], - }, - ], - ] - `); - }); - - it("doesn't add an upper-cased word when its lower-case will also be in the dictionary", async () => { - const existingWords = ["existing"]; - const unknownWords = ["abc", "Abc"]; - - mock$.mockResolvedValue({ - stdout: ` - file-1.ts Unknown word (${unknownWords[0]}) - file-2.ts Unknown word (${unknownWords[1]}) - `, - }); - - mockReadFile.mockResolvedValue(JSON.stringify({ words: existingWords })); - - await populateCSpellDictionary(); - - expect(mockFormatJson.mock.calls).toMatchInlineSnapshot(` - [ - [ - { - "words": [ - "abc", - "existing", - ], - }, - ], - ] - `); - }); -}); diff --git a/src/steps/populateCSpellDictionary.ts b/src/steps/populateCSpellDictionary.ts deleted file mode 100644 index 7166cb625..000000000 --- a/src/steps/populateCSpellDictionary.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { $ } from "execa"; -import * as fs from "node:fs/promises"; - -import { readFileAsJson } from "../shared/readFileAsJson.js"; -import { formatJson } from "./writing/creation/formatters/formatJson.js"; - -export async function populateCSpellDictionary() { - const { stdout } = await getStdout(); - const unknownWords = new Set( - Array.from(stdout.matchAll(/Unknown word \((.+)\)/g)).map( - ([, matched]) => matched, - ), - ); - - const existing = (await readFileAsJson("cspell.json")) as Partial< - typeof import("../../cspell.json") - >; - - const allWords = [...(existing.words ?? []), ...unknownWords]; - const allWordsUnique = new Set(allWords); - - await fs.writeFile( - "cspell.json", - await formatJson({ - ...existing, - words: allWords - .filter((word) => { - const wordLowerCase = word.toLowerCase(); - return word === wordLowerCase || !allWordsUnique.has(wordLowerCase); - }) - .sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())), - }), - ); -} - -async function getStdout() { - try { - return await $`pnpm run lint:spelling`; - } catch (error) { - return error as { stdout: string }; - } -} diff --git a/src/steps/removeSetupScripts.ts b/src/steps/removeSetupScripts.ts deleted file mode 100644 index 69bc66a80..000000000 --- a/src/steps/removeSetupScripts.ts +++ /dev/null @@ -1,29 +0,0 @@ -import * as fs from "node:fs/promises"; - -import { formatTypeScript } from "./writing/creation/formatters/formatTypeScript.js"; - -const globPaths = [ - "./bin", - "./docs", - "./script", - "./src/bin", - "./src/create", - "./src/initialize", - "./src/migrate", - "./src/next", - "./src/shared", - "./src/steps", -]; - -export async function removeSetupScripts() { - for (const globPath of globPaths) { - await fs.rm(globPath, { force: true, recursive: true }); - } - - await fs.writeFile( - "./src/index.ts", - await formatTypeScript( - [`export * from "./greet.js";`, `export * from "./types.js";`].join("\n"), - ), - ); -} diff --git a/src/steps/resetGitTags.ts b/src/steps/resetGitTags.ts deleted file mode 100644 index 59894fb5c..000000000 --- a/src/steps/resetGitTags.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { $ } from "execa"; - -export async function resetGitTags() { - const { stdout: allLocalTags } = await $`git tag -l`; - - // Create array of local tags by splitting the string at each new line and filtering out empty strings - const allLocalTagsArray = allLocalTags.split("\n").filter(Boolean); - - // Delete all local tags if there are any - if (allLocalTagsArray.length !== 0) { - await $`git tag -d ${allLocalTagsArray}`; - } -} diff --git a/src/steps/runCleanup.test.ts b/src/steps/runCleanup.test.ts deleted file mode 100644 index 70cc1fe4c..000000000 --- a/src/steps/runCleanup.test.ts +++ /dev/null @@ -1,71 +0,0 @@ -import chalk from "chalk"; -import { describe, expect, it, vi } from "vitest"; - -import { runCleanup } from "./runCleanup.js"; - -const mockExecaCommand = vi.fn().mockRejectedValue("Oh no!"); - -vi.mock("execa", () => ({ - get execaCommand() { - return mockExecaCommand; - }, -})); - -const mockLogLine = vi.fn(); - -vi.mock("../shared/cli/lines.js", () => ({ - get logLine() { - return mockLogLine; - }, -})); - -vi.mock("../shared/cli/spinners.js", () => ({ - withSpinner: vi.fn((_label: string, callback: () => unknown) => callback()), -})); - -describe("runCleanup", () => { - it("does not log when the commands all succeed", async () => { - mockExecaCommand.mockResolvedValue(undefined); - - await runCleanup(["first", "second"], "create"); - - expect(mockLogLine).not.toHaveBeenCalled(); - }); - - it("logs once when one command fails", async () => { - mockExecaCommand - .mockRejectedValueOnce(new Error("Oh no!")) - .mockResolvedValue(undefined); - - await runCleanup(["first", "second"], "create"); - - expect(mockLogLine).toHaveBeenCalledWith( - [ - chalk.yellow(`🟡 Running \``), - chalk.yellowBright("first"), - chalk.yellow(`\` failed. You should run it and fix its complaints.`), - ].join(""), - ); - }); - - it("logs twice when two commands fail", async () => { - mockExecaCommand.mockRejectedValue(new Error("Oh no!")); - - await runCleanup(["first", "second"], "create"); - - expect(mockLogLine).toHaveBeenCalledWith( - [ - chalk.yellow(`🟡 Running \``), - chalk.yellowBright("first"), - chalk.yellow(`\` failed. You should run it and fix its complaints.`), - ].join(""), - ); - expect(mockLogLine).toHaveBeenCalledWith( - [ - chalk.yellow(`🟡 Running \``), - chalk.yellowBright("second"), - chalk.yellow(`\` failed. You should run it and fix its complaints.`), - ].join(""), - ); - }); -}); diff --git a/src/steps/runCleanup.ts b/src/steps/runCleanup.ts deleted file mode 100644 index 79e441353..000000000 --- a/src/steps/runCleanup.ts +++ /dev/null @@ -1,43 +0,0 @@ -import chalk from "chalk"; -import { execaCommand } from "execa"; -import { rimraf } from "rimraf"; - -import { logLine } from "../shared/cli/lines.js"; -import { withSpinner } from "../shared/cli/spinners.js"; -import { Mode } from "../shared/types.js"; - -export async function runCleanup(commands: string[], mode: Mode) { - const failed: string[] = []; - - await withSpinner("Cleaning up files", async () => { - if (mode === "migrate") { - // Coverage folders can slow down format and lint times something awful. - // https://github.com/JoshuaKGoldberg/create-typescript-app/issues/1221 - await rimraf("coverage*"); - } - - for (const command of commands) { - try { - await execaCommand(command); - } catch { - failed.push(command); - } - } - }); - - if (!failed.length) { - return; - } - - logLine(); - - for (const command of failed) { - logLine( - [ - chalk.yellow(`🟡 Running \``), - chalk.yellowBright(command), - chalk.yellow(`\` failed. You should run it and fix its complaints.`), - ].join(""), - ); - } -} diff --git a/src/steps/uninstallPackages.test.ts b/src/steps/uninstallPackages.test.ts deleted file mode 100644 index 28c032849..000000000 --- a/src/steps/uninstallPackages.test.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; - -import { uninstallPackages } from "./uninstallPackages.js"; - -const mock$ = vi.fn(); - -vi.mock("execa", () => ({ - get $() { - return mock$; - }, -})); - -vi.mock("../shared/packages.js", () => ({ - readPackageData: () => ({}), - removeDependencies: vi.fn(), -})); - -describe("uninstallPackages", () => { - it("runs without --offline when offline is false", async () => { - await uninstallPackages(false); - - expect(mock$.mock.calls).toMatchInlineSnapshot(` - [ - [ - [ - "pnpm add prettier -D", - "", - ], - "", - ], - ] - `); - }); - - it("runs with --offline when offline is true", async () => { - await uninstallPackages(true); - - expect(mock$.mock.calls).toMatchInlineSnapshot(` - [ - [ - [ - "pnpm add prettier -D", - "", - ], - " --offline", - ], - ] - `); - }); -}); diff --git a/src/steps/uninstallPackages.ts b/src/steps/uninstallPackages.ts deleted file mode 100644 index 6dee58937..000000000 --- a/src/steps/uninstallPackages.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { $ } from "execa"; - -import { readPackageData, removeDependencies } from "../shared/packages.js"; - -export async function uninstallPackages(offline: boolean | undefined) { - const packageData = await readPackageData(); - - await removeDependencies( - [ - "@clack/prompts", - "@prettier/sync", - "all-contributors-cli", - "chalk", - "create", - "cspell-populate-words", - "execa", - "git-remote-origin-url", - "git-url-parse", - "html-to-text", - "image-size", - "input-from-file", - "input-from-file-json", - "input-from-script", - "js-yaml", - "lazy-value", - "npm-user", - "object-strings-deep", - "octokit", - "octokit-from-auth", - "parse-author", - "parse-package-name", - "populate-all-contributors-for-repository", - "prettier", - "remove-undefined-objects", - "replace-in-file", - "rimraf", - "set-github-repository-labels", - "sort-package-json", - "title-case", - "zod", - "zod-validation-error", - ], - packageData.dependencies, - ); - - await removeDependencies( - [ - "@octokit/request-error", - "@types/git-url-parse", - "@types/html-to-text", - "@types/js-yaml", - "@types/parse-author", - "all-contributors-cli", - "c8", - "create-testers", - "eslint-config-prettier", - "globby", - "tsx", - ], - packageData.devDependencies, - "-D", - ); - - await $`pnpm add prettier -D${offline ? " --offline" : ""}`; -} diff --git a/src/steps/updateAllContributorsTable.ts b/src/steps/updateAllContributorsTable.ts deleted file mode 100644 index 3c86dd1e8..000000000 --- a/src/steps/updateAllContributorsTable.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { $ } from "execa"; -import * as fs from "node:fs/promises"; - -import { readFileSafeAsJson } from "../shared/readFileSafeAsJson.js"; -import { AllContributorsData, Options } from "../shared/types.js"; -import { formatJson } from "./writing/creation/formatters/formatJson.js"; - -export async function updateAllContributorsTable({ - owner, - repository, -}: Pick) { - await fs.writeFile( - ".all-contributorsrc", - await formatJson({ - ...((await readFileSafeAsJson( - ".all-contributorsrc", - )) as AllContributorsData), - projectName: repository, - projectOwner: owner, - }), - ); - - await $`pnpx all-contributors-cli generate`; -} diff --git a/src/steps/updateLocalFiles.test.ts b/src/steps/updateLocalFiles.test.ts deleted file mode 100644 index c2b825989..000000000 --- a/src/steps/updateLocalFiles.test.ts +++ /dev/null @@ -1,550 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; - -import { Options } from "../shared/types.js"; -import { updateLocalFiles } from "./updateLocalFiles.js"; - -const mockReplaceInFile = vi.fn(); - -vi.mock("replace-in-file", () => ({ - get replaceInFile() { - return mockReplaceInFile; - }, -})); - -const mockReadFileSafeAsJson = vi.fn(); - -vi.mock("../shared/readFileSafeAsJson.js", () => ({ - get readFileSafeAsJson() { - return mockReadFileSafeAsJson; - }, -})); - -const options = { - access: "public", - base: "everything", - description: "Stub description.", - directory: ".", - email: { - github: "github@email.com", - npm: "npm@email.com", - }, - mode: "create", - offline: true, - owner: "StubOwner", - repository: "stub-repository", - title: "Stub Title", -} satisfies Options; - -describe("updateLocalFiles", () => { - it("throws a wrapping error when replaceInFiles rejects", async () => { - const error = new Error("Oh no!"); - - mockReadFileSafeAsJson.mockResolvedValue({}); - mockReplaceInFile.mockRejectedValue(error); - - await expect(async () => { - await updateLocalFiles({ ...options, mode: "initialize" }); - }).rejects.toThrowErrorMatchingInlineSnapshot( - `[Error: Failed to replace /Create TypeScript App/g with Stub Title in ./.github/**/*,./*.*]`, - ); - }); - - it("replaces using the common replacements when the existing package data is null", async () => { - mockReadFileSafeAsJson.mockResolvedValue(null); - mockReplaceInFile.mockResolvedValue([]); - - await updateLocalFiles({ ...options, mode: "initialize" }); - - expect(mockReplaceInFile.mock.calls).toMatchInlineSnapshot(` - [ - [ - { - "allowEmptyPaths": true, - "files": [ - "./.github/**/*", - "./*.*", - ], - "from": /Create TypeScript App/g, - "to": "Stub Title", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": [ - "./.github/**/*", - "./*.*", - ], - "from": /JoshuaKGoldberg\\(\\?:\\\\/\\(\\.\\+\\)\\)\\?/g, - "to": [Function], - }, - ], - [ - { - "allowEmptyPaths": true, - "files": "package.json", - "from": /JoshuaKGoldberg/g, - "to": "StubOwner", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": [ - "./.github/**/*", - "./*.*", - ], - "from": /create-typescript-app/g, - "to": "stub-repository", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": "eslint.config.js", - "from": /\\\\/\\\\\\*\\\\n\\.\\+\\\\\\*\\\\/\\\\n\\\\n/gs, - "to": "", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": "./package.json", - "from": /"author": "\\.\\+"/g, - "to": ""author": "undefined"", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": "./package.json", - "from": /"test:create": "\\.\\+\\\\n/g, - "to": "", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": "./package.json", - "from": /"test:initialize": "\\.\\*/g, - "to": "", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": "./package.json", - "from": /"initialize": "\\.\\*/g, - "to": "", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": "./package.json", - "from": /"test:migrate": "\\.\\+\\\\n/g, - "to": "", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": "./README.md", - "from": /## Getting Started\\.\\*## Development/gs, - "to": "## Development", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": "./.github/DEVELOPMENT.md", - "from": /\\\\n## Setup Scripts\\.\\*\\$/gs, - "to": "", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": "./knip.json", - "from": " "src/initialize/index.ts", - ", - "to": "", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": "./knip.json", - "from": " "src/migrate/index.ts", - ", - "to": "", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": "./knip.json", - "from": "["src/index.ts!", "script/initialize*.js"]", - "to": ""src/index.ts!"", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": "./knip.json", - "from": "["src/**/*.ts!", "script/**/*.js"]", - "to": ""src/**/*.ts!"", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": "./README.md", - "from": /> 💙 This package was templated with \\.\\+\\\\\\./g, - "to": "> 💙 This package was templated with [\`create-typescript-app\`](https://github.com/JoshuaKGoldberg/create-typescript-app).", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": "./package.json", - "from": /"bin": "\\.\\+\\\\n/g, - "to": "", - }, - ], - ] - `); - }); - - it("replaces using the common replacements when the existing package data is an empty object", async () => { - mockReadFileSafeAsJson.mockResolvedValue({}); - mockReplaceInFile.mockResolvedValue([]); - - await updateLocalFiles({ ...options, mode: "initialize" }); - - expect(mockReplaceInFile.mock.calls).toMatchInlineSnapshot(` - [ - [ - { - "allowEmptyPaths": true, - "files": [ - "./.github/**/*", - "./*.*", - ], - "from": /Create TypeScript App/g, - "to": "Stub Title", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": [ - "./.github/**/*", - "./*.*", - ], - "from": /JoshuaKGoldberg\\(\\?:\\\\/\\(\\.\\+\\)\\)\\?/g, - "to": [Function], - }, - ], - [ - { - "allowEmptyPaths": true, - "files": "package.json", - "from": /JoshuaKGoldberg/g, - "to": "StubOwner", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": [ - "./.github/**/*", - "./*.*", - ], - "from": /create-typescript-app/g, - "to": "stub-repository", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": "eslint.config.js", - "from": /\\\\/\\\\\\*\\\\n\\.\\+\\\\\\*\\\\/\\\\n\\\\n/gs, - "to": "", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": "./package.json", - "from": /"author": "\\.\\+"/g, - "to": ""author": "undefined"", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": "./package.json", - "from": /"test:create": "\\.\\+\\\\n/g, - "to": "", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": "./package.json", - "from": /"test:initialize": "\\.\\*/g, - "to": "", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": "./package.json", - "from": /"initialize": "\\.\\*/g, - "to": "", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": "./package.json", - "from": /"test:migrate": "\\.\\+\\\\n/g, - "to": "", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": "./README.md", - "from": /## Getting Started\\.\\*## Development/gs, - "to": "## Development", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": "./.github/DEVELOPMENT.md", - "from": /\\\\n## Setup Scripts\\.\\*\\$/gs, - "to": "", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": "./knip.json", - "from": " "src/initialize/index.ts", - ", - "to": "", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": "./knip.json", - "from": " "src/migrate/index.ts", - ", - "to": "", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": "./knip.json", - "from": "["src/index.ts!", "script/initialize*.js"]", - "to": ""src/index.ts!"", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": "./knip.json", - "from": "["src/**/*.ts!", "script/**/*.js"]", - "to": ""src/**/*.ts!"", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": "./README.md", - "from": /> 💙 This package was templated with \\.\\+\\\\\\./g, - "to": "> 💙 This package was templated with [\`create-typescript-app\`](https://github.com/JoshuaKGoldberg/create-typescript-app).", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": "./package.json", - "from": /"bin": "\\.\\+\\\\n/g, - "to": "", - }, - ], - ] - `); - }); - - it("doesn't remove existing tooling when mode is migrate", async () => { - mockReadFileSafeAsJson.mockResolvedValue({}); - mockReplaceInFile.mockResolvedValue([]); - - await updateLocalFiles({ ...options, mode: "migrate" }); - - expect(mockReplaceInFile.mock.calls).toMatchInlineSnapshot(` - [ - [ - { - "allowEmptyPaths": true, - "files": [ - "./.github/**/*", - "./*.*", - ], - "from": /Create TypeScript App/g, - "to": "Stub Title", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": [ - "./.github/**/*", - "./*.*", - ], - "from": /JoshuaKGoldberg\\(\\?:\\\\/\\(\\.\\+\\)\\)\\?/g, - "to": [Function], - }, - ], - [ - { - "allowEmptyPaths": true, - "files": "package.json", - "from": /JoshuaKGoldberg/g, - "to": "StubOwner", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": [ - "./.github/**/*", - "./*.*", - ], - "from": /create-typescript-app/g, - "to": "stub-repository", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": "eslint.config.js", - "from": /\\\\/\\\\\\*\\\\n\\.\\+\\\\\\*\\\\/\\\\n\\\\n/gs, - "to": "", - }, - ], - [ - { - "allowEmptyPaths": true, - "files": "./package.json", - "from": /"author": "\\.\\+"/g, - "to": ""author": "undefined"", - }, - ], - ] - `); - }); - - it("does not replace an existing description when it does not exist", async () => { - mockReadFileSafeAsJson.mockResolvedValue({}); - mockReplaceInFile.mockResolvedValue([]); - - await updateLocalFiles({ ...options, mode: "initialize" }); - - expect(mockReplaceInFile).not.toHaveBeenCalledWith({ - allowEmptyPaths: true, - files: ["./.github/**/*", "./*.*"], - from: expect.anything(), - to: options.description, - }); - }); - - it("replaces an existing description when it exists", async () => { - const existingDescription = "Existing description."; - - mockReadFileSafeAsJson.mockResolvedValue({ - description: existingDescription, - }); - mockReplaceInFile.mockResolvedValue([]); - - await updateLocalFiles({ ...options, mode: "initialize" }); - - expect(mockReplaceInFile).toHaveBeenCalledWith({ - allowEmptyPaths: true, - files: ["./.github/**/*", "./*.*"], - from: existingDescription, - to: options.description, - }); - }); - - it("removes bin when the mode is initialize", async () => { - mockReadFileSafeAsJson.mockResolvedValue({ - version: "1.2.3", - }); - mockReplaceInFile.mockResolvedValue([]); - - await updateLocalFiles({ ...options, mode: "initialize" }); - - expect(mockReplaceInFile).toHaveBeenCalledWith({ - allowEmptyPaths: true, - files: "./package.json", - from: /"bin": ".+\n/g, - to: "", - }); - }); - - it("does not remove bin when the mode is migrate", async () => { - mockReadFileSafeAsJson.mockResolvedValue({ - version: "1.2.3", - }); - mockReplaceInFile.mockResolvedValue([]); - - await updateLocalFiles({ ...options, mode: "migrate" }); - - expect(mockReplaceInFile).not.toHaveBeenCalledWith({ - allowEmptyPaths: true, - files: "./package.json", - from: /"bin": ".+\n/g, - to: "", - }); - }); - - it("resets package version to 0.0.0 when mode is initialize", async () => { - mockReadFileSafeAsJson.mockResolvedValue({ - version: "1.2.3", - }); - mockReplaceInFile.mockResolvedValue([]); - - await updateLocalFiles({ ...options, mode: "initialize" }); - - expect(mockReplaceInFile).toHaveBeenCalledWith({ - allowEmptyPaths: true, - files: "./package.json", - from: /"version": "1.2.3"/g, - to: '"version": "0.0.0"', - }); - }); - - it("does not reset package version to 0.0.0 when mode is migrate", async () => { - mockReadFileSafeAsJson.mockResolvedValue({ - version: "1.2.3", - }); - mockReplaceInFile.mockResolvedValue([]); - - await updateLocalFiles({ ...options, mode: "migrate" }); - - expect(mockReplaceInFile).not.toHaveBeenCalledWith({ - allowEmptyPaths: true, - files: "./package.json", - from: /"version": "1.2.3"/g, - to: '"version": "0.0.0"', - }); - }); -}); diff --git a/src/steps/updateLocalFiles.ts b/src/steps/updateLocalFiles.ts deleted file mode 100644 index e5ddc0fbe..000000000 --- a/src/steps/updateLocalFiles.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { From, replaceInFile, To } from "replace-in-file"; - -import { readFileSafeAsJson } from "../shared/readFileSafeAsJson.js"; -import { Options } from "../shared/types.js"; -import { createJoshuaKGoldbergReplacement } from "./createJoshuaKGoldbergReplacement.js"; -import { endOfReadmeTemplateLine } from "./updateReadme.js"; - -interface ExistingPackageData { - description?: string; - version?: string; -} - -export async function updateLocalFiles(options: Options) { - const existingPackage = ((await readFileSafeAsJson("./package.json")) ?? - {}) as ExistingPackageData; - - const replacements: [From, To, (string | string[])?][] = [ - [/Create TypeScript App/g, options.title], - createJoshuaKGoldbergReplacement(options), - [/JoshuaKGoldberg/g, options.owner, "package.json"], - [/create-typescript-app/g, options.repository], - [/\/\*\n.+\*\/\n\n/gs, ``, "eslint.config.js"], - [/"author": ".+"/g, `"author": "${options.author}"`, "./package.json"], - ...(options.mode === "migrate" - ? [] - : ([ - // Only creating a new repository should remove these parts. - // Existing ones might coincidentally have them too. - [/"test:create": ".+\n/g, ``, "./package.json"], - [/"test:initialize": ".*/g, ``, "./package.json"], - [/"initialize": ".*/g, ``, "./package.json"], - [/"test:migrate": ".+\n/g, ``, "./package.json"], - [ - /## Getting Started.*## Development/gs, - `## Development`, - "./README.md", - ], - [/\n## Setup Scripts.*$/gs, "", "./.github/DEVELOPMENT.md"], - [`\t\t"src/initialize/index.ts",\n`, ``, "./knip.json"], - [`\t\t"src/migrate/index.ts",\n`, ``, "./knip.json"], - [ - `["src/index.ts!", "script/initialize*.js"]`, - `"src/index.ts!"`, - "./knip.json", - ], - [ - `["src/**/*.ts!", "script/**/*.js"]`, - `"src/**/*.ts!"`, - "./knip.json", - ], - // Edge case: migration scripts will rewrite README.md attribution - [ - /> 💙 This package was templated with .+\./g, - endOfReadmeTemplateLine, - "./README.md", - ], - ] as typeof replacements)), - ]; - - if (existingPackage.description) { - replacements.push([existingPackage.description, options.description]); - } - - if (options.mode !== "migrate") { - replacements.push([/"bin": ".+\n/g, ``, "./package.json"]); - - if (options.mode === "initialize" && existingPackage.version) { - replacements.push([ - new RegExp(`"version": "${existingPackage.version}"`, "g"), - `"version": "0.0.0"`, - "./package.json", - ]); - } - } - - for (const [from, to, files = ["./.github/**/*", "./*.*"]] of replacements) { - try { - await replaceInFile({ - allowEmptyPaths: true, - files, - from, - to, - }); - } catch (error) { - const toString = typeof to === "function" ? "(function)" : to; - throw new Error( - `Failed to replace ${from.toString()} with ${toString} in ${files.toString()}`, - { - cause: error, - }, - ); - } - } -} diff --git a/src/steps/updateReadme.test.ts b/src/steps/updateReadme.test.ts deleted file mode 100644 index bd7ca4744..000000000 --- a/src/steps/updateReadme.test.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; - -import { updateReadme } from "./updateReadme.js"; - -const mockWriteFile = vi.fn(); - -vi.mock("node:fs/promises", () => ({ - get writeFile() { - return mockWriteFile; - }, -})); - -const mockReadFileSafe = vi.fn(); - -vi.mock("../shared/readFileSafe.js", () => ({ - get readFileSafe() { - return mockReadFileSafe; - }, -})); - -const options = { - owner: "NewOwner", -}; - -describe("updateReadme", () => { - it("adds a notice when the file does not contain it already and excludeTemplatedBy is not enabled", async () => { - mockReadFileSafe.mockResolvedValue( - "Existing JoshuaKGoldberg/create-typescript-app content.", - ); - - await updateReadme(options); - - expect(mockWriteFile.mock.calls).toMatchInlineSnapshot(` - [ - [ - "./README.md", - "Existing NewOwner/create-typescript-app content. - - - > 💙 This package was templated with [\`create-typescript-app\`](https://github.com/JoshuaKGoldberg/create-typescript-app). - ", - ], - ] - `); - }); - - it("doesn't add a notice when excludeTemplatedBy is enabled", async () => { - mockReadFileSafe.mockResolvedValue( - "Existing JoshuaKGoldberg/create-typescript-app content.", - ); - - await updateReadme({ ...options, excludeTemplatedBy: true }); - - expect(mockWriteFile.mock.calls).toMatchInlineSnapshot(` - [ - [ - "./README.md", - "Existing NewOwner/create-typescript-app content.", - ], - ] - `); - }); - - it("doesn't add a notice when the file contains it already", async () => { - mockReadFileSafe.mockResolvedValue(` -Existing JoshuaKGoldberg/create-typescript-app content. - - - -> 💙 This package was templated using [create-typescript-app](https://github.com/JoshuaKGoldberg/create-typescript-app). -`); - - await updateReadme(options); - - expect(mockWriteFile.mock.calls).toMatchInlineSnapshot(` - [ - [ - "./README.md", - " - Existing NewOwner/create-typescript-app content. - - - - > 💙 This package was templated using [create-typescript-app](https://github.com/NewOwner/create-typescript-app). - ", - ], - ] - `); - }); - - it("doesn't add a notice when the file contains an older version of it already", async () => { - mockReadFileSafe.mockResolvedValue(` -Existing JoshuaKGoldberg/create-typescript-app content. - -💙 This package is based on [@JoshuaKGoldberg](https://github.com/JoshuaKGoldberg)'s [create-typescript-app](https://github.com/JoshuaKGoldberg/create-typescript-app). -`); - - await updateReadme(options); - - expect(mockWriteFile.mock.calls).toMatchInlineSnapshot(` - [ - [ - "./README.md", - " - Existing NewOwner/create-typescript-app content. - - 💙 This package is based on [@NewOwner](https://github.com/NewOwner)'s [create-typescript-app](https://github.com/NewOwner/create-typescript-app). - ", - ], - ] - `); - }); - - it("removes the project logo when it exists", async () => { - mockReadFileSafe.mockResolvedValue(` -Existing JoshuaKGoldberg/create-typescript-app content. - -Project logo: the TypeScript blue square with rounded corners, but a plus sign instead of 'TS' - -💙 This package is based on [@JoshuaKGoldberg](https://github.com/JoshuaKGoldberg)'s [create-typescript-app](https://github.com/JoshuaKGoldberg/create-typescript-app). - `); - - await updateReadme(options); - - expect(mockWriteFile.mock.calls).toMatchInlineSnapshot(` - [ - [ - "./README.md", - " - Existing NewOwner/create-typescript-app content. - - 💙 This package is based on [@NewOwner](https://github.com/NewOwner)'s [create-typescript-app](https://github.com/NewOwner/create-typescript-app). - ", - ], - ] - `); - }); -}); diff --git a/src/steps/updateReadme.ts b/src/steps/updateReadme.ts deleted file mode 100644 index 744ef1413..000000000 --- a/src/steps/updateReadme.ts +++ /dev/null @@ -1,34 +0,0 @@ -import * as fs from "node:fs/promises"; -import { EOL } from "node:os"; - -import { readFileSafe } from "../shared/readFileSafe.js"; -import { Options } from "../shared/types.js"; - -export const endOfReadmeTemplateLine = `> 💙 This package was templated with [\`create-typescript-app\`](https://github.com/JoshuaKGoldberg/create-typescript-app).`; - -export const endOfReadmeNotice = [ - ``, - ``, - ``, - endOfReadmeTemplateLine, - ``, -].join(EOL); - -export const endOfReadmeMatcher = - /💙.+(?:based|built|templated).+(?:from|using|on|with).+create-typescript-app/; - -export async function updateReadme( - options: Pick, -) { - let contents = await readFileSafe("./README.md", ""); - - contents = contents - .replaceAll("JoshuaKGoldberg", options.owner) - .replace(/\nProject logo.+>\n/gs, { - describe("no result cases", () => { - test.each([ - "", - "abc123", - "# Test Title", - "[]", - "[][]", - "[]()", - "[][]()()", - ``, - ])("%j", (input) => { - expect(findExistingBadges(input)).toEqual([]); - }); - }); - - describe("single result cases", () => { - test.each([ - `[![GitHub CI](https://github.com/ExampleOwner/console-fail-test/actions/workflows/compile.yml/badge.svg)](https://github.com/ExampleOwner/console-fail-test/actions/workflows/compile.yml)`, - `[![Code Style: Prettier](https://img.shields.io/badge/code_style-prettier-brightgreen.svg)](https://prettier.io)`, - `![TypeScript: Strict](https://img.shields.io/badge/typescript-strict-brightgreen.svg)`, - `[![NPM version](https://badge.fury.io/js/console-fail-test.svg)](http://badge.fury.io/js/console-fail-test)`, - `[![Downloads](http://img.shields.io/npm/dm/console-fail-test.svg)](https://npmjs.org/package/console-fail-test)`, - "badge", - "badge", - "badge", - "badge", - ` - - - All Contributors: 1 - - - `, - ` Codecov Test Coverage`, - ` Contributor Covenant`, - `📦 npm version`, - `🧪 Coverage`, - `🤝 Code of Conduct: kept`, - `📝 License: MIT`, - `License: MIT`, - ` - License: MIT - `, - `💪 TypeScript: Strict`, - `Style: Prettier`, - `TypeScript: Strict`, - ` - TypeScript: Strict - `, - ])("%s", (contents) => { - expect(findExistingBadges(contents)).toEqual([contents.trim()]); - }); - }); - - it("doesn't collect badges after a ##", () => { - expect( - findExistingBadges(` - test badge a - - ## Usage - - test badge b - `), - ).toEqual([`test badge a`]); - }); - - it("doesn't collect badges after an h2", () => { - expect( - findExistingBadges(` - test badge a - -

Usage

- - test badge b - `), - ).toEqual([`test badge a`]); - }); - - test("real-world usage", () => { - expect( - findExistingBadges(` -

- - - All Contributors: 1 - - - Codecov Test Coverage - Contributor Covenant - License: MIT - Style: Prettier - TypeScript: Strict -

- `), - ).toMatchInlineSnapshot(` - [ - "All Contributors: 1", - "Codecov Test Coverage", - "Contributor Covenant", - "License: MIT", - "Style: Prettier", - "TypeScript: Strict", - ] - `); - }); -}); diff --git a/src/steps/writeReadme/findExistingBadges.ts b/src/steps/writeReadme/findExistingBadges.ts deleted file mode 100644 index f91672542..000000000 --- a/src/steps/writeReadme/findExistingBadges.ts +++ /dev/null @@ -1,30 +0,0 @@ -export const existingBadgeMatcherCreators = [ - () => /\[!\[.+\]\(.+\)\]\(.+\)/g, - () => /!\[.+\]\(.+\)/g, - () => /^\s*[\s\S]+?<\/a>/gm, - () => //g, -]; - -export function findExistingBadges(contents: string): string[] { - const badges: string[] = []; - let remaining = contents.split(/<\s*h2.*>|##/)[0]; - - for (const createMatcher of existingBadgeMatcherCreators) { - while (true) { - const matcher = createMatcher(); - const matched = matcher.exec(remaining); - - if (!matched) { - break; - } - - const [badge] = matched; - - badges.push(badge.trim()); - - remaining = remaining.replace(badge, ""); - } - } - - return badges; -} diff --git a/src/steps/writeReadme/findIntroSectionClose.test.ts b/src/steps/writeReadme/findIntroSectionClose.test.ts deleted file mode 100644 index fc6381a61..000000000 --- a/src/steps/writeReadme/findIntroSectionClose.test.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { describe, expect, it } from "vitest"; - -import { findIntroSectionClose } from "./findIntroSectionClose.js"; - -describe("findIntroSectionClose", () => { - it.each([ - [ - `# First -## Second`, - 6, - ], - [ - `# First -

Second

`, - 6, - ], - [ - `# First -

Second

`, - 6, - ], - [ - `# First -\`\`\`js -... -\`\`\``, - 6, - ], - [ - `# First - -[![](https://img.shields.io/badge/abc-ffcc00.svg)](image.jpg) - -[![](https://img.shields.io/badge/abc-ffcc00.svg)](image.jpg) -`, - 135, - ], - [ - `Plain heading - -Next line. -`, - 14, - ], - [ - `

Title

- -

Description.

- -

- (existing badges) -

- -Project logo: ... - -First intro text. - -## Getting Started -`, - 173, - ], - ])("%o", (contents, expected) => { - expect(findIntroSectionClose(contents)).toEqual(expected); - }); -}); diff --git a/src/steps/writeReadme/findIntroSectionClose.ts b/src/steps/writeReadme/findIntroSectionClose.ts deleted file mode 100644 index 27d8bffdb..000000000 --- a/src/steps/writeReadme/findIntroSectionClose.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { existingBadgeMatcherCreators } from "./findExistingBadges.js"; - -export function findIntroSectionClose(contents: string) { - // Highest priority: after an existing create-typescript-app-style logo - const projectLogoMatch = - /Project logo.+/.exec(contents); - if (projectLogoMatch) { - return contents.indexOf("\n", projectLogoMatch.index) + 2; - } - - // Next: before a first code block or h2, presumably following badges - const indexOfH2OrCodeBlock = contents.search(/## |<\s*h2|```/); - - if (indexOfH2OrCodeBlock !== -1) { - return indexOfH2OrCodeBlock - 2; - } - - // Failing those, if any badges are found, go after the last of them - for (const createMatcher of existingBadgeMatcherCreators) { - const lastMatch = [...contents.matchAll(createMatcher())].at(-1); - - if (lastMatch?.index) { - return lastMatch.index + lastMatch[0].length + 2; - } - } - - // Lastly, go for the second line altogether - return contents.indexOf("\n", 1) + 1; -} diff --git a/src/steps/writeReadme/generateTopContent.test.ts b/src/steps/writeReadme/generateTopContent.test.ts deleted file mode 100644 index 1e9c136ba..000000000 --- a/src/steps/writeReadme/generateTopContent.test.ts +++ /dev/null @@ -1,147 +0,0 @@ -import { describe, expect, it } from "vitest"; - -import { Options } from "../../shared/types.js"; -import { generateTopContent } from "./generateTopContent.js"; - -const optionsBase = { - access: "public", - description: "Test description", - directory: ".", - email: { - github: "github@email.com", - npm: "npm@email.com", - }, - mode: "create", - owner: "test-owner", - repository: "test-repository", - title: "Test Title", -} satisfies Options; - -describe("findExistingBadges", () => { - it("generates full contents when there are no existing badges", () => { - expect(generateTopContent(optionsBase, [])).toMatchInlineSnapshot(` - "

Test Title

- -

Test description

- -

- - - 👪 All Contributors: 1 - - - 🤝 Code of Conduct: Kept - 🧪 Coverage - 📝 License: MIT - 📦 npm version - 💪 TypeScript: Strict -

- - ## Usage - - \`\`\`shell - npm i test-repository - \`\`\` - \`\`\`ts - import { greet } from "test-repository"; - - greet("Hello, world! 💖"); - \`\`\`" - `); - }); - - it("replaces existing contents when there is an existing known badge", () => { - expect( - generateTopContent(optionsBase, [ - `TypeScript: Strict`, - ]), - ).toMatchInlineSnapshot(` - "

Test Title

- -

Test description

- -

- - - 👪 All Contributors: 1 - - - 🤝 Code of Conduct: Kept - 🧪 Coverage - 📝 License: MIT - 📦 npm version - 💪 TypeScript: Strict -

- - ## Usage - - \`\`\`shell - npm i test-repository - \`\`\` - \`\`\`ts - import { greet } from "test-repository"; - - greet("Hello, world! 💖"); - \`\`\`" - `); - }); - - it("push existing badges to the end when there is an existing unknown badge", () => { - expect( - generateTopContent(optionsBase, [ - `Unknown Badge`, - ]), - ).toMatchInlineSnapshot(` - "

Test Title

- -

Test description

- -

- - - 👪 All Contributors: 1 - - - 🤝 Code of Conduct: Kept - 🧪 Coverage - 📝 License: MIT - 📦 npm version - 💪 TypeScript: Strict - Unknown Badge -

- - ## Usage - - \`\`\`shell - npm i test-repository - \`\`\` - \`\`\`ts - import { greet } from "test-repository"; - - greet("Hello, world! 💖"); - \`\`\`" - `); - }); - - it("does not include a greet section when the mode is migrate", () => { - expect(generateTopContent({ ...optionsBase, mode: "migrate" }, [])) - .toMatchInlineSnapshot(` - "

Test Title

- -

Test description

- -

- - - 👪 All Contributors: 1 - - - 🤝 Code of Conduct: Kept - 🧪 Coverage - 📝 License: MIT - 📦 npm version - 💪 TypeScript: Strict -

" - `); - }); -}); diff --git a/src/steps/writeReadme/generateTopContent.ts b/src/steps/writeReadme/generateTopContent.ts deleted file mode 100644 index 04c04eac6..000000000 --- a/src/steps/writeReadme/generateTopContent.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { Options } from "../../shared/types.js"; - -export function generateTopContent(options: Options, existingBadges: string[]) { - const remainingExistingBadges = new Set(existingBadges); - const badges: string[] = []; - - for (const [badgeLine, existingMatcher] of [ - [ - !options.excludeAllContributors && - ` - - 👪 All Contributors: 1 - - `, - /🤝 Code of Conduct: Kept`, - /CODE_OF_CONDUCT\.md/, - ], - [ - !options.excludeTests && - `🧪 Coverage`, - /https:\/\/codecov\.io\/gh/, - ], - [ - `📝 License: MIT`, - /LICENSE\.(md|txt)/, - ], - [ - `📦 npm version`, - /npm.*v/i, - ], - [ - `💪 TypeScript: Strict`, - /typescript.*strict/i, - ], - ] as const) { - const existingMatch = existingBadges.find((existingLine) => - existingMatcher.test(existingLine), - ); - - if (existingMatch) { - remainingExistingBadges.delete(existingMatch); - } - - if (badgeLine) { - badges.push(badgeLine); - } - } - - return `

${options.title}

- -

${options.description}

- -

-${[...badges, ...remainingExistingBadges] - .map((badge) => `\t${badge}`) - .join("\n")} -

${ - options.logo - ? ` - -${options.logo.alt} - -` - : "" - }${ - options.mode === "migrate" - ? "" - : ` - -## Usage - -\`\`\`shell -npm i ${options.repository} -\`\`\` -\`\`\`ts -import { greet } from "${options.repository}"; - -greet("Hello, world! 💖"); -\`\`\`` - }`; -} diff --git a/src/steps/writeReadme/index.test.ts b/src/steps/writeReadme/index.test.ts deleted file mode 100644 index 22c45587f..000000000 --- a/src/steps/writeReadme/index.test.ts +++ /dev/null @@ -1,319 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; - -import { Options } from "../../shared/types.js"; -import { writeReadme } from "./index.js"; - -const mockWriteFile = vi.fn(); - -vi.mock("node:fs/promises", () => ({ - get writeFile() { - return mockWriteFile; - }, -})); - -const mockReadFileSafe = vi.fn(); - -vi.mock("../../shared/readFileSafe.js", () => ({ - get readFileSafe() { - return mockReadFileSafe; - }, -})); - -const options = { - access: "public", - author: "Test Author", - base: "everything", - description: "Test description.", - directory: ".", - email: { - github: "github@email.com", - npm: "npm@email.com", - }, - funding: "TestFunding", - mode: "create", - owner: "TestOwner", - repository: "test-repository", - title: "Test Title", -} satisfies Options; - -describe("writeReadme", () => { - it("writes a new file when the README.md does not yet exist", async () => { - mockReadFileSafe.mockResolvedValueOnce(""); - - await writeReadme(options); - - expect(mockWriteFile.mock.calls).toMatchInlineSnapshot(` - [ - [ - "README.md", - "

Test Title

- -

Test description.

- -

- - - 👪 All Contributors: 1 - - - 🤝 Code of Conduct: Kept - 🧪 Coverage - 📝 License: MIT - 📦 npm version - 💪 TypeScript: Strict -

- - ## Usage - - \`\`\`shell - npm i test-repository - \`\`\` - \`\`\`ts - import { greet } from "test-repository"; - - greet("Hello, world! 💖"); - \`\`\` - - ## Contributors - - - - - - -
- - - - - - - - > 💙 This package was templated with [\`create-typescript-app\`](https://github.com/JoshuaKGoldberg/create-typescript-app). - ", - ], - ] - `); - }); - - it("adds sections when the README.md already exists and is sparse", async () => { - mockReadFileSafe.mockResolvedValueOnce(`# ${options.title}\n`); - - await writeReadme(options); - - expect(mockWriteFile.mock.calls).toMatchInlineSnapshot(` - [ - [ - "README.md", - "

Test Title

- -

Test description.

- -

- - - 👪 All Contributors: 1 - - - 🤝 Code of Conduct: Kept - 🧪 Coverage - 📝 License: MIT - 📦 npm version - 💪 TypeScript: Strict -

- - ## Usage - - \`\`\`shell - npm i test-repository - \`\`\` - \`\`\`ts - import { greet } from "test-repository"; - - greet("Hello, world! 💖"); - \`\`\` - - ## Contributors - - - - - - -
- - - - - - - - > 💙 This package was templated with [\`create-typescript-app\`](https://github.com/JoshuaKGoldberg/create-typescript-app). - ", - ], - ] - `); - }); - - it("adds all-contributors content when directed to and the indicator does not yet exist", async () => { - mockReadFileSafe.mockResolvedValueOnce(`# ${options.title}\n`); - - await writeReadme({ - ...options, - excludeAllContributors: false, - }); - - expect(mockWriteFile.mock.calls).toMatchInlineSnapshot(` - [ - [ - "README.md", - "

Test Title

- -

Test description.

- -

- - - 👪 All Contributors: 1 - - - 🤝 Code of Conduct: Kept - 🧪 Coverage - 📝 License: MIT - 📦 npm version - 💪 TypeScript: Strict -

- - ## Usage - - \`\`\`shell - npm i test-repository - \`\`\` - \`\`\`ts - import { greet } from "test-repository"; - - greet("Hello, world! 💖"); - \`\`\` - - ## Contributors - - - - - - -
- - - - - - - - > 💙 This package was templated with [\`create-typescript-app\`](https://github.com/JoshuaKGoldberg/create-typescript-app). - ", - ], - ] - `); - }); - - it("does not duplicate sections when the README.md already exists and has them", async () => { - mockReadFileSafe.mockResolvedValueOnce(`

Test Title

- -

Test description.

- -

- - - All Contributors: 2 - - - - Contributor Covenant - License: MIT - Style: Prettier - TypeScript: Strict -

- - -## Contributors - - - - - - - -
- - - - - - - - - -> 💙 This package is based on [@JoshuaKGoldberg](https://github.com/JoshuaKGoldberg)'s [create-typescript-app](https://github.com/JoshuaKGoldberg/create-typescript-app). -`); - - await writeReadme(options); - - expect(mockWriteFile.mock.calls).toMatchInlineSnapshot(` - [ - [ - "README.md", - "

Test Title

- -

Test description.

- -

- - - 👪 All Contributors: 1 - - - 🤝 Code of Conduct: Kept - 🧪 Coverage - 📝 License: MIT - 📦 npm version - 💪 TypeScript: Strict - Style: Prettier -

- - ## Usage - - \`\`\`shell - npm i test-repository - \`\`\` - \`\`\`ts - import { greet } from "test-repository"; - - greet("Hello, world! 💖"); - \`\`\` - - ## Contributors - - - - - - - -
- - - - - - - - - - > 💙 This package is based on [@JoshuaKGoldberg](https://github.com/JoshuaKGoldberg)'s [create-typescript-app](https://github.com/JoshuaKGoldberg/create-typescript-app). - ", - ], - ] - `); - }); -}); diff --git a/src/steps/writeReadme/index.ts b/src/steps/writeReadme/index.ts deleted file mode 100644 index bbcb61507..000000000 --- a/src/steps/writeReadme/index.ts +++ /dev/null @@ -1,73 +0,0 @@ -import * as fs from "node:fs/promises"; - -import { readFileSafe } from "../../shared/readFileSafe.js"; -import { Options } from "../../shared/types.js"; -import { endOfReadmeMatcher, endOfReadmeNotice } from "../updateReadme.js"; -import { findExistingBadges } from "./findExistingBadges.js"; -import { findIntroSectionClose } from "./findIntroSectionClose.js"; -import { generateTopContent } from "./generateTopContent.js"; - -const contributorsIndicator = ``; - -export async function writeReadme(options: Options) { - const allContributorsContent = - !options.excludeAllContributors && generateAllContributorsContent(options); - let contents = await readFileSafe("README.md", ""); - if (!contents) { - await fs.writeFile( - "README.md", - [ - generateTopContent(options, []), - allContributorsContent, - endOfReadmeNotice.slice(1), - ] - .filter(Boolean) - .join("\n\n"), - ); - return; - } - - const endOfIntroSection = findIntroSectionClose(contents); - - contents = [ - generateTopContent(options, findExistingBadges(contents)), - contents.slice(endOfIntroSection), - ] - .join("") - .replace(/\[!\[.+\]\(.+\)\]\(.+\)/g, "") - .replace(/!\[.+\]\(.+\)/g, "") - .replaceAll("\r", "") - .replaceAll("\n\n\n", "\n\n"); - - if (allContributorsContent && !contents.includes(contributorsIndicator)) { - contents = [contents, allContributorsContent].join("\n\n"); - } - - if (!endOfReadmeMatcher.test(contents)) { - contents = [contents, endOfReadmeNotice].join("\n"); - } - - await fs.writeFile("README.md", contents); -} - -function generateAllContributorsContent(options: Options) { - return [ - `## Contributors`, - ``, - ``, - contributorsIndicator, - ``, - !options.excludeLintMd && ``, - ``, - ``, - `
`, - ``, - !options.excludeLintMd && ``, - ``, - ``, - ``, - ``, - ] - .filter(Boolean) - .join("\n"); -} diff --git a/src/steps/writing/creation/createCSpellConfig.test.ts b/src/steps/writing/creation/createCSpellConfig.test.ts deleted file mode 100644 index b52ecff2e..000000000 --- a/src/steps/writing/creation/createCSpellConfig.test.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { describe, expect, it } from "vitest"; - -import { createCSpellConfig } from "./createCSpellConfig.js"; - -describe("createCSpellConfig", () => { - it("creates an ignore file with all words when exclusions are disabled", async () => { - const actual = await createCSpellConfig({}); - - expect(actual).toMatchInlineSnapshot(` - "{ - "dictionaries": ["npm", "node", "typescript"], - "ignorePaths": [ - ".all-contributorsrc", - ".github", - "CHANGELOG.md", - "coverage", - "lib", - "node_modules", - "pnpm-lock.yaml" - ], - "words": [ - "apexskier", - "automerge", - "joshuakgoldberg", - "markdownlintignore", - "tseslint" - ] - } - " - `); - }); - - it("creates an ignore file with minimal words when exclusions are enabled", async () => { - const actual = await createCSpellConfig({ - excludeAllContributors: true, - excludeLintMd: true, - excludeReleases: true, - excludeRenovate: true, - excludeTemplatedBy: true, - excludeTests: true, - }); - - expect(actual).toMatchInlineSnapshot(` - "{ - "dictionaries": ["npm", "node", "typescript"], - "ignorePaths": [ - ".github", - "CHANGELOG.md", - "lib", - "node_modules", - "pnpm-lock.yaml" - ], - "words": ["tseslint"] - } - " - `); - }); -}); diff --git a/src/steps/writing/creation/createCSpellConfig.ts b/src/steps/writing/creation/createCSpellConfig.ts deleted file mode 100644 index 4ebae5b1d..000000000 --- a/src/steps/writing/creation/createCSpellConfig.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Options } from "../../../shared/types.js"; -import { formatJson } from "./formatters/formatJson.js"; - -export async function createCSpellConfig( - options: Pick< - Options, - | "excludeAllContributors" - | "excludeLintMd" - | "excludeReleases" - | "excludeRenovate" - | "excludeTemplatedBy" - | "excludeTests" - >, -) { - const words = [ - "tseslint", - !options.excludeReleases && "apexskier", - !options.excludeRenovate && "automerge", - !options.excludeLintMd && "markdownlintignore", - !options.excludeTemplatedBy && "joshuakgoldberg", - ] - .filter(Boolean) - .sort(); - - return await formatJson({ - dictionaries: ["npm", "node", "typescript"], - ignorePaths: [ - ...(options.excludeAllContributors ? [] : [".all-contributorsrc"]), - ".github", - "CHANGELOG.md", - ...(options.excludeTests ? [] : ["coverage"]), - "lib", - "node_modules", - "pnpm-lock.yaml", - ], - words, - }); -} diff --git a/src/steps/writing/creation/createDotGitignore.test.ts b/src/steps/writing/creation/createDotGitignore.test.ts deleted file mode 100644 index 1ce565fe1..000000000 --- a/src/steps/writing/creation/createDotGitignore.test.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { describe, expect, it } from "vitest"; - -import { createDotGitignore } from "./createDotGitignore.js"; - -describe("createDotGitignore", () => { - it("creates an ignore file with coverage when excludeTests is false", () => { - const actual = createDotGitignore({ excludeTests: false }); - - expect(actual).toMatchInlineSnapshot(` - "/coverage - /lib - /node_modules - " - `); - }); - - it("creates an ignore file without coverage when excludeTests is true", () => { - const actual = createDotGitignore({ excludeTests: true }); - - expect(actual).toMatchInlineSnapshot(` - "/lib - /node_modules - " - `); - }); -}); diff --git a/src/steps/writing/creation/createDotGitignore.ts b/src/steps/writing/creation/createDotGitignore.ts deleted file mode 100644 index 06a94620e..000000000 --- a/src/steps/writing/creation/createDotGitignore.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Options } from "../../../shared/types.js"; -import { formatIgnoreFile } from "./formatters/formatIgnoreFile.js"; - -export function createDotGitignore(options: Pick) { - return formatIgnoreFile( - [ - ...(options.excludeTests ? [] : ["/coverage"]), - "/lib", - "/node_modules", - ].sort(), - ); -} diff --git a/src/steps/writing/creation/createESLintConfig.test.ts b/src/steps/writing/creation/createESLintConfig.test.ts deleted file mode 100644 index 27c8e3cde..000000000 --- a/src/steps/writing/creation/createESLintConfig.test.ts +++ /dev/null @@ -1,172 +0,0 @@ -import { describe, expect, it } from "vitest"; - -import { Options } from "../../../shared/types.js"; -import { createESLintConfig } from "./createESLintConfig.js"; - -function fakeOptions(getExcludeValue: (exclusionName: string) => boolean) { - return { - access: "public", - author: "TestAuthor", - base: "everything", - description: "Test description.", - directory: ".", - email: { - github: "github@email.com", - npm: "npm@email.com", - }, - ...Object.fromEntries( - [ - "excludeCompliance", - "excludeAllContributors", - "excludeLintESLint", - "excludeLintJSDoc", - "excludeLintJson", - "excludeLintKnip", - "excludeLintMd", - "excludeLintPackageJson", - "excludeLintPackages", - "excludeLintPerfectionist", - "excludeLintRegexp", - "excludeLintSpelling", - "excludeLintStrict", - "excludeLintStylistic", - "excludeLintYml", - "excludeReleases", - "excludeRenovate", - "excludeTests", - ].map((key) => [key, getExcludeValue(key)]), - ), - mode: "create", - owner: "TestOwner", - repository: "test-repository", - skipGitHubApi: true, - skipInstall: true, - skipRemoval: true, - title: "Test Title", - } satisfies Options; -} - -describe("createESLintConfig", () => { - it("creates a minimal config when all exclusions are enabled", async () => { - expect(await createESLintConfig(fakeOptions(() => true))) - .toMatchInlineSnapshot(` - "import eslint from "@eslint/js"; - import n from "eslint-plugin-n"; - import tseslint from "typescript-eslint"; - - export default tseslint.config( - { ignores: ["lib", "node_modules", "pnpm-lock.yaml"] }, - { linterOptions: { reportUnusedDisableDirectives: "error" } }, - eslint.configs.recommended, - n.configs["flat/recommended"], - { - extends: tseslint.configs.recommendedTypeChecked, - files: ["**/*.js", "**/*.ts"], - languageOptions: { - parserOptions: { - projectService: { allowDefaultProject: ["*.config.*s"] }, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - extends: [tseslint.configs.disableTypeChecked], - files: ["**/*.md/*.ts"], - rules: { - "n/no-missing-import": ["error", { allowModules: ["test-repository"] }], - }, - }, - ); - " - `); - }); - - it("creates a full config when all exclusions are disabled", async () => { - expect(await createESLintConfig(fakeOptions(() => false))) - .toMatchInlineSnapshot(` - "import comments from "@eslint-community/eslint-plugin-eslint-comments/configs"; - import eslint from "@eslint/js"; - import vitest from "@vitest/eslint-plugin"; - import jsdoc from "eslint-plugin-jsdoc"; - import jsonc from "eslint-plugin-jsonc"; - import markdown from "eslint-plugin-markdown"; - import n from "eslint-plugin-n"; - import packageJson from "eslint-plugin-package-json/configs/recommended"; - import perfectionist from "eslint-plugin-perfectionist"; - import * as regexp from "eslint-plugin-regexp"; - import yml from "eslint-plugin-yml"; - import tseslint from "typescript-eslint"; - - export default tseslint.config( - { - ignores: ["**/*.snap", "coverage", "lib", "node_modules", "pnpm-lock.yaml"], - }, - { linterOptions: { reportUnusedDisableDirectives: "error" } }, - eslint.configs.recommended, - comments.recommended, - jsdoc.configs["flat/contents-typescript-error"], - jsdoc.configs["flat/logical-typescript-error"], - jsdoc.configs["flat/stylistic-typescript-error"], - jsonc.configs["flat/recommended-with-json"], - markdown.configs.recommended, - n.configs["flat/recommended"], - packageJson, - perfectionist.configs["recommended-natural"], - regexp.configs["flat/recommended"], - { - extends: [ - tseslint.configs.strictTypeChecked, - tseslint.configs.stylisticTypeChecked, - ], - files: ["**/*.js", "**/*.ts"], - languageOptions: { - parserOptions: { - projectService: { allowDefaultProject: ["*.config.*s"] }, - tsconfigRootDir: import.meta.dirname, - }, - }, - rules: { - // Stylistic concerns that don't interfere with Prettier - "logical-assignment-operators": [ - "error", - "always", - { enforceForIfStatements: true }, - ], - "no-useless-rename": "error", - "object-shorthand": "error", - "operator-assignment": "error", - }, - settings: { perfectionist: { partitionByComment: true, type: "natural" } }, - }, - { - extends: [tseslint.configs.disableTypeChecked], - files: ["**/*.md/*.ts"], - rules: { - "n/no-missing-import": ["error", { allowModules: ["test-repository"] }], - }, - }, - { - extends: [vitest.configs.recommended], - files: ["**/*.test.*"], - rules: { "@typescript-eslint/no-unsafe-assignment": "off" }, - }, - { - extends: [yml.configs["flat/recommended"], yml.configs["flat/prettier"]], - files: ["**/*.{yml,yaml}"], - rules: { - "yml/file-extension": ["error", { extension: "yml" }], - "yml/sort-keys": [ - "error", - { order: { type: "asc" }, pathPattern: "^.*$" }, - ], - "yml/sort-sequence-values": [ - "error", - { order: { type: "asc" }, pathPattern: "^.*$" }, - ], - }, - }, - ); - " - `); - }); -}); diff --git a/src/steps/writing/creation/createESLintConfig.ts b/src/steps/writing/creation/createESLintConfig.ts deleted file mode 100644 index a7891d5eb..000000000 --- a/src/steps/writing/creation/createESLintConfig.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { Options } from "../../../shared/types.js"; -import { formatTypeScript } from "./formatters/formatTypeScript.js"; - -export async function createESLintConfig(options: Options) { - const tseslintBase = options.excludeLintStrict ? "recommended" : "strict"; - - const imports = [ - !options.excludeLintESLint && - `import comments from "@eslint-community/eslint-plugin-eslint-comments/configs";`, - `import eslint from "@eslint/js";`, - !options.excludeTests && `import vitest from "@vitest/eslint-plugin";`, - !options.excludeLintJSDoc && `import jsdoc from "eslint-plugin-jsdoc";`, - !options.excludeLintJson && `import jsonc from "eslint-plugin-jsonc";`, - !options.excludeLintMd && `import markdown from "eslint-plugin-markdown";`, - `import n from "eslint-plugin-n";`, - !options.excludeLintPackageJson && - `import packageJson from "eslint-plugin-package-json/configs/recommended";`, - !options.excludeLintPerfectionist && - `import perfectionist from "eslint-plugin-perfectionist";`, - !options.excludeLintRegexp && - `import * as regexp from "eslint-plugin-regexp";`, - !options.excludeLintYml && `import yml from "eslint-plugin-yml";`, - `import tseslint from "typescript-eslint";`, - ].filter(Boolean); - - const elements = [ - `eslint.configs.recommended,`, - !options.excludeLintESLint && ` comments.recommended,`, - !options.excludeLintJSDoc && - ` jsdoc.configs["flat/contents-typescript-error"], - jsdoc.configs["flat/logical-typescript-error"], - jsdoc.configs["flat/stylistic-typescript-error"],`, - !options.excludeLintJson && ` jsonc.configs["flat/recommended-with-json"],`, - !options.excludeLintMd && ` markdown.configs.recommended,`, - ` n.configs["flat/recommended"],`, - !options.excludeLintPackageJson && ` packageJson,`, - !options.excludeLintPerfectionist && - ` perfectionist.configs["recommended-natural"],`, - !options.excludeLintRegexp && ` regexp.configs["flat/recommended"],`, - ].filter(Boolean); - - const ignores = [ - ...(options.excludeTests ? [] : ["**/*.snap", "coverage"]), - "lib", - "node_modules", - "pnpm-lock.yaml", - ] - .map((ignore) => JSON.stringify(ignore)) - .sort(); - - const rules = - !options.excludeLintStylistic && - `{ - // Stylistic concerns that don't interfere with Prettier - "logical-assignment-operators": [ - "error", - "always", - { enforceForIfStatements: true }, - ], - "no-useless-rename": "error", - "object-shorthand": "error", - "operator-assignment": "error", - }`; - - return await formatTypeScript(`${imports.join("\n")} - -export default tseslint.config( - { ignores: [${ignores.join(", ")}], }, - { linterOptions: { reportUnusedDisableDirectives: "error" } }, - ${elements.join("\n")} - { - extends: ${ - options.excludeLintStylistic - ? `tseslint.configs.${tseslintBase}TypeChecked` - : `[ - tseslint.configs.${tseslintBase}TypeChecked, - tseslint.configs.stylisticTypeChecked, - ]` - }, - files: ["**/*.js", "**/*.ts"], - languageOptions: { - parserOptions: { - projectService: { allowDefaultProject: ["*.config.*s"] }, - tsconfigRootDir: import.meta.dirname - }, - },${ - rules - ? ` - rules: ${rules},` - : "" - }${ - options.excludeLintPerfectionist - ? "" - : ` - settings: { perfectionist: { partitionByComment: true, type: "natural" } },` - } - }, - { - extends: [tseslint.configs.disableTypeChecked], - files: ["**/*.md/*.ts"], - rules: { - "n/no-missing-import": [ - "error", - { allowModules: ["${options.repository}"] }, - ], - }, - },${ - options.excludeTests - ? "" - : ` - { - extends: [vitest.configs.recommended], - files: ["**/*.test.*"], - rules: { "@typescript-eslint/no-unsafe-assignment": "off" }, - },` - }${ - options.excludeLintYml - ? "" - : ` - { - extends: [yml.configs["flat/recommended"], yml.configs["flat/prettier"]], - files: ["**/*.{yml,yaml}"], - rules: { - "yml/file-extension": ["error", { extension: "yml" }], - "yml/sort-keys": [ - "error", - { order: { type: "asc" }, pathPattern: "^.*$" }, - ], - "yml/sort-sequence-values": [ - "error", - { order: { type: "asc" }, pathPattern: "^.*$" }, - ], - }, - },` - } -); -`); -} diff --git a/src/steps/writing/creation/createKnipConfig.test.ts b/src/steps/writing/creation/createKnipConfig.test.ts deleted file mode 100644 index 884e5e6cb..000000000 --- a/src/steps/writing/creation/createKnipConfig.test.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; - -import { createKnipConfig } from "./createKnipConfig.js"; - -const mockReadFileSafeAsJson = vi.fn(); - -vi.mock("../../../shared/readFileSafeAsJson.js", () => ({ - get readFileSafeAsJson() { - return mockReadFileSafeAsJson; - }, -})); - -describe("createKnipConfig", () => { - it("uses uses the knip version from package.json devDependencies when it exists", async () => { - const version = "1.2.3"; - mockReadFileSafeAsJson.mockResolvedValueOnce({ - devDependencies: { knip: version }, - }); - - const packageJson = await createKnipConfig(); - - expect(JSON.parse(packageJson)).toEqual({ - $schema: `https://unpkg.com/knip@${version}/schema.json`, - entry: ["src/index.ts!"], - ignoreExportsUsedInFile: { - interface: true, - type: true, - }, - project: ["src/**/*.ts!"], - }); - }); - - it("uses version 'latest' when the package.json does not exist", async () => { - mockReadFileSafeAsJson.mockResolvedValueOnce(undefined); - - const packageJson = await createKnipConfig(); - - expect(JSON.parse(packageJson)).toEqual({ - $schema: `https://unpkg.com/knip@latest/schema.json`, - entry: ["src/index.ts!"], - ignoreExportsUsedInFile: { - interface: true, - type: true, - }, - project: ["src/**/*.ts!"], - }); - }); - - it("uses version 'latest' when the package.json exists but does not have knip in devDependencies", async () => { - mockReadFileSafeAsJson.mockResolvedValueOnce({ - dependencies: {}, - }); - - const packageJson = await createKnipConfig(); - - expect(JSON.parse(packageJson)).toEqual({ - $schema: `https://unpkg.com/knip@latest/schema.json`, - entry: ["src/index.ts!"], - ignoreExportsUsedInFile: { - interface: true, - type: true, - }, - project: ["src/**/*.ts!"], - }); - }); -}); diff --git a/src/steps/writing/creation/createKnipConfig.ts b/src/steps/writing/creation/createKnipConfig.ts deleted file mode 100644 index ba7d41a3a..000000000 --- a/src/steps/writing/creation/createKnipConfig.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { readFileSafeAsJson } from "../../../shared/readFileSafeAsJson.js"; -import { PartialPackageData } from "../../../shared/types.js"; -import { formatJson } from "./formatters/formatJson.js"; - -export async function createKnipConfig() { - const existingPackageJson = (await readFileSafeAsJson( - "./package.json", - )) as null | PartialPackageData; - - return await formatJson({ - $schema: `https://unpkg.com/knip@${existingPackageJson?.devDependencies?.knip ?? "latest"}/schema.json`, - entry: ["src/index.ts!"], - ignoreExportsUsedInFile: { - interface: true, - type: true, - }, - project: ["src/**/*.ts!"], - }); -} diff --git a/src/steps/writing/creation/createTsupConfig.test.ts b/src/steps/writing/creation/createTsupConfig.test.ts deleted file mode 100644 index d95b331b9..000000000 --- a/src/steps/writing/creation/createTsupConfig.test.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { describe, expect, it } from "vitest"; - -import { createTsupConfig } from "./createTsupConfig.js"; - -describe("createTsupConfig", () => { - it("creates an ignore file with test entries when excludeTests is false", async () => { - const actual = await createTsupConfig({ excludeTests: false }); - - expect(actual).toMatchInlineSnapshot(` - "import { defineConfig } from "tsup"; - - export default defineConfig({ - bundle: false, - clean: true, - dts: true, - entry: ["src/**/*.ts", "!src/**/*.test.*"], - format: "esm", - outDir: "lib", - sourcemap: true, - }); - " - `); - }); - - it("creates an ignore file without test entries when excludeTests is true", async () => { - const actual = await createTsupConfig({ excludeTests: true }); - - expect(actual).toMatchInlineSnapshot(` - "import { defineConfig } from "tsup"; - - export default defineConfig({ - bundle: false, - clean: true, - dts: true, - entry: ["src/**/*.ts"], - format: "esm", - outDir: "lib", - sourcemap: true, - }); - " - `); - }); -}); diff --git a/src/steps/writing/creation/createTsupConfig.ts b/src/steps/writing/creation/createTsupConfig.ts deleted file mode 100644 index 15166818c..000000000 --- a/src/steps/writing/creation/createTsupConfig.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Options } from "../../../shared/types.js"; -import { formatTypeScript } from "./formatters/formatTypeScript.js"; - -export async function createTsupConfig(options: Pick) { - return await formatTypeScript(`import { defineConfig } from "tsup"; - -export default defineConfig({ - bundle: false, - clean: true, - dts: true, - entry: ["src/**/*.ts"${options.excludeTests ? "" : `, "!src/**/*.test.*"`}], - format: "esm", - outDir: "lib", - sourcemap: true, -}); -`); -} diff --git a/src/steps/writing/creation/dotGitHub/actions.test.ts b/src/steps/writing/creation/dotGitHub/actions.test.ts deleted file mode 100644 index d2f648d01..000000000 --- a/src/steps/writing/creation/dotGitHub/actions.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { describe, expect, it } from "vitest"; - -import { createDotGitHubActions } from "./actions.js"; - -describe("createDotGitHubActions", () => { - it("creates a prepare/action.yml file", () => { - const actual = createDotGitHubActions(); - - expect(actual).toEqual({ - prepare: { - "action.yml": `description: Prepares the repo for a typical CI job - -name: Prepare - -runs: - steps: - - uses: pnpm/action-setup@v4 - with: - version: 9 - - uses: actions/setup-node@v4 - with: - cache: pnpm - node-version: '20' - - run: pnpm install --frozen-lockfile - shell: bash - using: composite -`, - }, - }); - }); -}); diff --git a/src/steps/writing/creation/dotGitHub/actions.ts b/src/steps/writing/creation/dotGitHub/actions.ts deleted file mode 100644 index 3df2c87c9..000000000 --- a/src/steps/writing/creation/dotGitHub/actions.ts +++ /dev/null @@ -1,31 +0,0 @@ -import jsYaml from "js-yaml"; - -export function createDotGitHubActions() { - return { - prepare: { - "action.yml": jsYaml - .dump({ - description: "Prepares the repo for a typical CI job", - name: "Prepare", - runs: { - steps: [ - { - uses: "pnpm/action-setup@v4", - with: { version: 9 }, - }, - { - uses: "actions/setup-node@v4", - with: { cache: "pnpm", "node-version": "20" }, - }, - { - run: "pnpm install --frozen-lockfile", - shell: "bash", - }, - ], - using: "composite", - }, - }) - .replaceAll(/\n(\S)/g, "\n\n$1"), - }, - }; -} diff --git a/src/steps/writing/creation/dotGitHub/createDevelopment/index.test.ts b/src/steps/writing/creation/dotGitHub/createDevelopment/index.test.ts deleted file mode 100644 index 0dd4598ab..000000000 --- a/src/steps/writing/creation/dotGitHub/createDevelopment/index.test.ts +++ /dev/null @@ -1,403 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; - -import { Options } from "../../../../../shared/types.js"; -import { createDevelopment } from "./index.js"; - -const mockReadFileSafe = vi.fn(); - -vi.mock("../../../../../shared/readFileSafe.js", () => ({ - get readFileSafe() { - return mockReadFileSafe; - }, -})); - -const options = { - access: "public", - author: "Test Author", - base: "everything", - description: "Test description.", - directory: ".", - email: { - github: "github@email.com", - npm: "npm@email.com", - }, - mode: "create", - owner: "TestOwner", - repository: "test-repository", - skipInstall: true, - title: "Test Title", -} satisfies Options; - -describe("createDevelopment", () => { - it("creates a file with extra options turned on when options disable them", async () => { - mockReadFileSafe.mockResolvedValue(""); - - const actual = await createDevelopment({ - ...options, - excludeLintKnip: false, - excludeLintMd: false, - excludeLintPackageJson: false, - excludeLintPackages: false, - excludeLintSpelling: false, - }); - - expect(actual).toMatchInlineSnapshot(` - "# Development - - After [forking the repo from GitHub](https://help.github.com/articles/fork-a-repo) and [installing pnpm](https://pnpm.io/installation): - - \`\`\`shell - git clone https://github.com//test-repository - cd test-repository - pnpm install - \`\`\` - - > This repository includes a list of suggested VS Code extensions. - > It's a good idea to use [VS Code](https://code.visualstudio.com) and accept its suggestion to install them, as they'll help with development. - - ## Building - - Run [**tsup**](https://tsup.egoist.dev) locally to build source files from \`src/\` into output files in \`lib/\`: - - \`\`\`shell - pnpm build - \`\`\` - - Add \`--watch\` to run the builder in a watch mode that continuously cleans and recreates \`lib/\` as you save files: - - \`\`\`shell - pnpm build --watch - \`\`\` - - ## Formatting - - [Prettier](https://prettier.io) is used to format code. - It should be applied automatically when you save files in VS Code or make a Git commit. - - To manually reformat all files, you can run: - - \`\`\`shell - pnpm format --write - \`\`\` - - ## Linting - - This package includes several forms of linting to enforce consistent code quality and styling. - Each should be shown in VS Code, and can be run manually on the command-line: - - - \`pnpm lint\` ([ESLint](https://eslint.org) with [typescript-eslint](https://typescript-eslint.io)): Lints JavaScript and TypeScript source files - - \`pnpm lint:knip\` ([knip](https://github.com/webpro/knip)): Detects unused files, dependencies, and code exports - - \`pnpm lint:md\` ([Markdownlint](https://github.com/DavidAnson/markdownlint)): Checks Markdown source files - - \`pnpm lint:packages\` ([pnpm dedupe --check](https://pnpm.io/cli/dedupe)): Checks for unnecessarily duplicated packages in the \`pnpm-lock.yml\` file - - \`pnpm lint:spelling\` ([cspell](https://cspell.org)): Spell checks across all source files - - Read the individual documentation for each linter to understand how it can be configured and used best. - - For example, ESLint can be run with \`--fix\` to auto-fix some lint rule complaints: - - \`\`\`shell - pnpm run lint --fix - \`\`\` - - Note that you'll need to run \`pnpm build\` before \`pnpm lint\` so that lint rules which check the file system can pick up on any built files. - - ## Testing - - [Vitest](https://vitest.dev) is used for tests. - You can run it locally on the command-line: - - \`\`\`shell - pnpm run test - \`\`\` - - Add the \`--coverage\` flag to compute test coverage and place reports in the \`coverage/\` directory: - - \`\`\`shell - pnpm run test --coverage - \`\`\` - - Note that [console-fail-test](https://github.com/JoshuaKGoldberg/console-fail-test) is enabled for all test runs. - Calls to \`console.log\`, \`console.warn\`, and other console methods will cause a test to fail. - - ### Debugging Tests - - This repository includes a [VS Code launch configuration](https://code.visualstudio.com/docs/editor/debugging) for debugging unit tests. - To launch it, open a test file, then run _Debug Current Test File_ from the VS Code Debug panel (or press F5). - - ## Type Checking - - You should be able to see suggestions from [TypeScript](https://typescriptlang.org) in your editor for all open files. - - However, it can be useful to run the TypeScript command-line (\`tsc\`) to type check all files in \`src/\`: - - \`\`\`shell - pnpm tsc - \`\`\` - - Add \`--watch\` to keep the type checker running in a watch mode that updates the display as you save files: - - \`\`\`shell - pnpm tsc --watch - \`\`\` - " - `); - }); - - it("creates a file with extra options turned off when options enable them", async () => { - mockReadFileSafe.mockResolvedValue(""); - - const actual = await createDevelopment({ - ...options, - excludeLintKnip: true, - excludeLintMd: true, - excludeLintPackageJson: true, - excludeLintPackages: true, - excludeLintSpelling: true, - guide: { - href: "https://example.com", - title: "Example Guide", - }, - }); - - expect(actual).toMatchInlineSnapshot(` - "# Development - - > If you'd like a more guided walkthrough, see [Example Guide](https://example.com). - > It'll walk you through the common activities you'll need to contribute. - - After [forking the repo from GitHub](https://help.github.com/articles/fork-a-repo) and [installing pnpm](https://pnpm.io/installation): - - \`\`\`shell - git clone https://github.com//test-repository - cd test-repository - pnpm install - \`\`\` - - > This repository includes a list of suggested VS Code extensions. - > It's a good idea to use [VS Code](https://code.visualstudio.com) and accept its suggestion to install them, as they'll help with development. - - ## Building - - Run [**tsup**](https://tsup.egoist.dev) locally to build source files from \`src/\` into output files in \`lib/\`: - - \`\`\`shell - pnpm build - \`\`\` - - Add \`--watch\` to run the builder in a watch mode that continuously cleans and recreates \`lib/\` as you save files: - - \`\`\`shell - pnpm build --watch - \`\`\` - - ## Formatting - - [Prettier](https://prettier.io) is used to format code. - It should be applied automatically when you save files in VS Code or make a Git commit. - - To manually reformat all files, you can run: - - \`\`\`shell - pnpm format --write - \`\`\` - - ## Linting - - [ESLint](https://eslint.org) is used with with [typescript-eslint](https://typescript-eslint.io)) to lint JavaScript and TypeScript source files. - You can run it locally on the command-line: - - \`\`\`shell - pnpm run lint - \`\`\` - - ESLint can be run with \`--fix\` to auto-fix some lint rule complaints: - - \`\`\`shell - pnpm run lint --fix - \`\`\` - - Note that you'll need to run \`pnpm build\` before \`pnpm lint\` so that lint rules which check the file system can pick up on any built files. - - ## Testing - - [Vitest](https://vitest.dev) is used for tests. - You can run it locally on the command-line: - - \`\`\`shell - pnpm run test - \`\`\` - - Add the \`--coverage\` flag to compute test coverage and place reports in the \`coverage/\` directory: - - \`\`\`shell - pnpm run test --coverage - \`\`\` - - Note that [console-fail-test](https://github.com/JoshuaKGoldberg/console-fail-test) is enabled for all test runs. - Calls to \`console.log\`, \`console.warn\`, and other console methods will cause a test to fail. - - ### Debugging Tests - - This repository includes a [VS Code launch configuration](https://code.visualstudio.com/docs/editor/debugging) for debugging unit tests. - To launch it, open a test file, then run _Debug Current Test File_ from the VS Code Debug panel (or press F5). - - ## Type Checking - - You should be able to see suggestions from [TypeScript](https://typescriptlang.org) in your editor for all open files. - - However, it can be useful to run the TypeScript command-line (\`tsc\`) to type check all files in \`src/\`: - - \`\`\`shell - pnpm tsc - \`\`\` - - Add \`--watch\` to keep the type checker running in a watch mode that updates the display as you save files: - - \`\`\`shell - pnpm tsc --watch - \`\`\` - " - `); - }); - - it("preserves existing sections when they don't match the new sections", async () => { - mockReadFileSafe.mockResolvedValue(`## Existing One - -Abc 123. - -## Building - -Will be removed. - -## Tests - -Will be removed. - -## Existing Two - -Def 456. -`); - - const actual = await createDevelopment({ - ...options, - excludeLintKnip: false, - excludeLintMd: false, - excludeLintPackageJson: false, - excludeLintPackages: false, - excludeLintSpelling: false, - }); - - expect(actual).toMatchInlineSnapshot(` - "# Development - - After [forking the repo from GitHub](https://help.github.com/articles/fork-a-repo) and [installing pnpm](https://pnpm.io/installation): - - \`\`\`shell - git clone https://github.com//test-repository - cd test-repository - pnpm install - \`\`\` - - > This repository includes a list of suggested VS Code extensions. - > It's a good idea to use [VS Code](https://code.visualstudio.com) and accept its suggestion to install them, as they'll help with development. - - ## Building - - Run [**tsup**](https://tsup.egoist.dev) locally to build source files from \`src/\` into output files in \`lib/\`: - - \`\`\`shell - pnpm build - \`\`\` - - Add \`--watch\` to run the builder in a watch mode that continuously cleans and recreates \`lib/\` as you save files: - - \`\`\`shell - pnpm build --watch - \`\`\` - - ## Formatting - - [Prettier](https://prettier.io) is used to format code. - It should be applied automatically when you save files in VS Code or make a Git commit. - - To manually reformat all files, you can run: - - \`\`\`shell - pnpm format --write - \`\`\` - - ## Linting - - This package includes several forms of linting to enforce consistent code quality and styling. - Each should be shown in VS Code, and can be run manually on the command-line: - - - \`pnpm lint\` ([ESLint](https://eslint.org) with [typescript-eslint](https://typescript-eslint.io)): Lints JavaScript and TypeScript source files - - \`pnpm lint:knip\` ([knip](https://github.com/webpro/knip)): Detects unused files, dependencies, and code exports - - \`pnpm lint:md\` ([Markdownlint](https://github.com/DavidAnson/markdownlint)): Checks Markdown source files - - \`pnpm lint:packages\` ([pnpm dedupe --check](https://pnpm.io/cli/dedupe)): Checks for unnecessarily duplicated packages in the \`pnpm-lock.yml\` file - - \`pnpm lint:spelling\` ([cspell](https://cspell.org)): Spell checks across all source files - - Read the individual documentation for each linter to understand how it can be configured and used best. - - For example, ESLint can be run with \`--fix\` to auto-fix some lint rule complaints: - - \`\`\`shell - pnpm run lint --fix - \`\`\` - - Note that you'll need to run \`pnpm build\` before \`pnpm lint\` so that lint rules which check the file system can pick up on any built files. - - ## Testing - - [Vitest](https://vitest.dev) is used for tests. - You can run it locally on the command-line: - - \`\`\`shell - pnpm run test - \`\`\` - - Add the \`--coverage\` flag to compute test coverage and place reports in the \`coverage/\` directory: - - \`\`\`shell - pnpm run test --coverage - \`\`\` - - Note that [console-fail-test](https://github.com/JoshuaKGoldberg/console-fail-test) is enabled for all test runs. - Calls to \`console.log\`, \`console.warn\`, and other console methods will cause a test to fail. - - ### Debugging Tests - - This repository includes a [VS Code launch configuration](https://code.visualstudio.com/docs/editor/debugging) for debugging unit tests. - To launch it, open a test file, then run _Debug Current Test File_ from the VS Code Debug panel (or press F5). - - ## Type Checking - - You should be able to see suggestions from [TypeScript](https://typescriptlang.org) in your editor for all open files. - - However, it can be useful to run the TypeScript command-line (\`tsc\`) to type check all files in \`src/\`: - - \`\`\`shell - pnpm tsc - \`\`\` - - Add \`--watch\` to keep the type checker running in a watch mode that updates the display as you save files: - - \`\`\`shell - pnpm tsc --watch - \`\`\` - - ## Existing One - - Abc 123. - - ## Tests - - Will be removed. - - ## Existing Two - - Def 456. - " - `); - }); -}); diff --git a/src/steps/writing/creation/dotGitHub/createDevelopment/index.ts b/src/steps/writing/creation/dotGitHub/createDevelopment/index.ts deleted file mode 100644 index 5ce29ce1e..000000000 --- a/src/steps/writing/creation/dotGitHub/createDevelopment/index.ts +++ /dev/null @@ -1,177 +0,0 @@ -import { readFileSafe } from "../../../../../shared/readFileSafe.js"; -import { Options } from "../../../../../shared/types.js"; -import { splitIntoSections } from "./splitIntoSections.js"; - -const headingAliases = new Map([ - ["build", "Building"], - ["builds", "Building"], - ["format", "Formatting"], - ["lint", "Linting"], - ["test", "Testing"], - ["tests", "Testing"], -]); - -export async function createDevelopment(options: Options) { - const existingContents = await readFileSafe(".github/DEVELOPMENT.md", ""); - - const newSections = { - "## Building": `Run [**tsup**](https://tsup.egoist.dev) locally to build source files from \`src/\` into output files in \`lib/\`: - -\`\`\`shell -pnpm build -\`\`\` - -Add \`--watch\` to run the builder in a watch mode that continuously cleans and recreates \`lib/\` as you save files: - -\`\`\`shell -pnpm build --watch -\`\`\``, - ...(options.bin && { - "### Built App Debugging": `This repository includes a [VS Code launch configuration](https://code.visualstudio.com/docs/editor/debugging) for debugging. -To debug a \`bin\` app, add a breakpoint to your code, then run _Debug Program_ from the VS Code Debug panel (or press F5). -VS Code will automatically run the \`build\` task in the background before running \`${options.bin}\`.`, - }), - "## Formatting": `[Prettier](https://prettier.io) is used to format code. -It should be applied automatically when you save files in VS Code or make a Git commit. - -To manually reformat all files, you can run: - -\`\`\`shell -pnpm format --write -\`\`\``, - "## Linting": `${createLintingSection(options)} - -\`\`\`shell -pnpm run lint --fix -\`\`\` - -Note that you'll need to run \`pnpm build\` before \`pnpm lint\` so that lint rules which check the file system can pick up on any built files.`, - ...(!options.excludeTests && { - "## Testing": `[Vitest](https://vitest.dev) is used for tests. -You can run it locally on the command-line: - -\`\`\`shell -pnpm run test -\`\`\` - -Add the \`--coverage\` flag to compute test coverage and place reports in the \`coverage/\` directory: - -\`\`\`shell -pnpm run test --coverage -\`\`\` - -Note that [console-fail-test](https://github.com/JoshuaKGoldberg/console-fail-test) is enabled for all test runs. -Calls to \`console.log\`, \`console.warn\`, and other console methods will cause a test to fail.`, - - // Debugging tests are intentionally put second - "### Debugging Tests": `This repository includes a [VS Code launch configuration](https://code.visualstudio.com/docs/editor/debugging) for debugging unit tests. -To launch it, open a test file, then run _Debug Current Test File_ from the VS Code Debug panel (or press F5).`, - }), - "## Type Checking": `You should be able to see suggestions from [TypeScript](https://typescriptlang.org) in your editor for all open files. - -However, it can be useful to run the TypeScript command-line (\`tsc\`) to type check all files in \`src/\`: - -\`\`\`shell -pnpm tsc -\`\`\` - -Add \`--watch\` to keep the type checker running in a watch mode that updates the display as you save files: - -\`\`\`shell -pnpm tsc --watch -\`\`\``, - }; - - const newSectionHeadings = new Set([ - "Development", - Object.keys(newSections).map((key) => key.replace(/^#* /, "")), - ]); - - const existingSectionsSplit = splitIntoSections(existingContents); - const preservedSectionsSplit = existingSectionsSplit.filter(([key]) => { - const keyText = key.replace(/^#* /, ""); - return !newSectionHeadings.has( - headingAliases.get(keyText.toLowerCase()) ?? keyText, - ); - }); - const preservedSections = Object.fromEntries(preservedSectionsSplit); - - const sectionLines: string[] = []; - const seen = new Set(); - - for (const sections of [newSections, preservedSections]) { - for (const heading in sections) { - if (seen.has(heading)) { - continue; - } - - seen.add(heading); - - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const contents = sections[heading as keyof typeof sections]!; - - sectionLines.push(""); - sectionLines.push(heading); - sectionLines.push(""); - sectionLines.push(contents); - } - } - - const result = `# Development -${ - options.guide - ? ` -> If you'd like a more guided walkthrough, see [${options.guide.title}](${options.guide.href}). -> It'll walk you through the common activities you'll need to contribute. -` - : "" -} -After [forking the repo from GitHub](https://help.github.com/articles/fork-a-repo) and [installing pnpm](https://pnpm.io/installation): - -\`\`\`shell -git clone https://github.com//${options.repository} -cd ${options.repository} -pnpm install -\`\`\` - -> This repository includes a list of suggested VS Code extensions. -> It's a good idea to use [VS Code](https://code.visualstudio.com) and accept its suggestion to install them, as they'll help with development. -${sectionLines.join("\n")} -`; - - return result; -} - -function createLintingSection(options: Options) { - const lintLines = [ - !options.excludeLintKnip && - `- \`pnpm lint:knip\` ([knip](https://github.com/webpro/knip)): Detects unused files, dependencies, and code exports`, - !options.excludeLintMd && - `- \`pnpm lint:md\` ([Markdownlint](https://github.com/DavidAnson/markdownlint)): Checks Markdown source files`, - !options.excludeLintPackages && - `- \`pnpm lint:packages\` ([pnpm dedupe --check](https://pnpm.io/cli/dedupe)): Checks for unnecessarily duplicated packages in the \`pnpm-lock.yml\` file`, - !options.excludeLintSpelling && - `- \`pnpm lint:spelling\` ([cspell](https://cspell.org)): Spell checks across all source files`, - ].filter(Boolean); - - return lintLines.length - ? [ - `This package includes several forms of linting to enforce consistent code quality and styling.`, - `Each should be shown in VS Code, and can be run manually on the command-line:`, - ``, - `- \`pnpm lint\` ([ESLint](https://eslint.org) with [typescript-eslint](https://typescript-eslint.io)): Lints JavaScript and TypeScript source files`, - ...lintLines, - ``, - `Read the individual documentation for each linter to understand how it can be configured and used best.`, - ``, - `For example, ESLint can be run with \`--fix\` to auto-fix some lint rule complaints:`, - ].join("\n") - : `[ESLint](https://eslint.org) is used with with [typescript-eslint](https://typescript-eslint.io)) to lint JavaScript and TypeScript source files. -You can run it locally on the command-line: - -\`\`\`shell -pnpm run lint -\`\`\` - -ESLint can be run with \`--fix\` to auto-fix some lint rule complaints:`; -} diff --git a/src/steps/writing/creation/dotGitHub/createDevelopment/splitIntoSections.test.ts b/src/steps/writing/creation/dotGitHub/createDevelopment/splitIntoSections.test.ts deleted file mode 100644 index dc2a01816..000000000 --- a/src/steps/writing/creation/dotGitHub/createDevelopment/splitIntoSections.test.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { describe, expect, test } from "vitest"; - -import { splitIntoSections } from "./splitIntoSections.js"; - -describe("createDevelopment", () => { - test.each([ - ["", []], - ["# Development \nabc 123", [["# Development", "abc 123"]]], - ["# Development \n\nabc 123 ", [["# Development", "abc 123"]]], - ["# Development \n Indented. ", [["# Development", "Indented."]]], - ["# Development \n Indented. ", [["# Development", "Indented."]]], - ["# Development \n\tIndented. ", [["# Development", "Indented."]]], - [ - `# Development - -. - -## Abc - -abc 123 - -### Def - -def 456 - -## Ghi - -ghi 789 -`, - [ - ["# Development", "."], - ["## Abc", "abc 123"], - ["### Def", "def 456"], - ["## Ghi", "ghi 789"], - ], - ], - ])("%j", (text, expected) => { - expect(splitIntoSections(text)).toEqual(expected); - }); -}); diff --git a/src/steps/writing/creation/dotGitHub/createDevelopment/splitIntoSections.ts b/src/steps/writing/creation/dotGitHub/createDevelopment/splitIntoSections.ts deleted file mode 100644 index 4b2cf3741..000000000 --- a/src/steps/writing/creation/dotGitHub/createDevelopment/splitIntoSections.ts +++ /dev/null @@ -1,27 +0,0 @@ -export function splitIntoSections(text: string) { - const sections: [string, string][] = []; - if (!text) { - return sections; - } - - let remaining = `${text}\n`; - - while (remaining) { - const indexOfNewline = remaining.indexOf("\n", 1); - let nextStart = remaining.indexOf("\n#", 1); - if (nextStart === -1) { - nextStart = remaining.length; - } - - const heading = remaining.slice(0, indexOfNewline).trim(); - const contents = remaining - .slice(indexOfNewline, nextStart) - .trim() - .replace(/^\n+/, ""); - - sections.push([heading, contents]); - remaining = remaining.slice(nextStart); - } - - return sections; -} diff --git a/src/steps/writing/creation/dotGitHub/createDotGitHubFiles.test.ts b/src/steps/writing/creation/dotGitHub/createDotGitHubFiles.test.ts deleted file mode 100644 index aab930544..000000000 --- a/src/steps/writing/creation/dotGitHub/createDotGitHubFiles.test.ts +++ /dev/null @@ -1,600 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; - -import { Options } from "../../../../shared/types.js"; -import { createDotGitHubFiles } from "./createDotGitHubFiles.js"; - -vi.mock("./createDevelopment/index.js", () => ({ - createDevelopment: () => "# Development!", -})); - -const createOptions = (exclude: boolean) => - ({ - access: "public", - base: "everything", - bin: exclude ? undefined : "./bin/index.js", - description: "Test description.", - directory: ".", - email: { - github: "github@email.com", - npm: "npm@email.com", - }, - excludeAllContributors: exclude, - excludeCompliance: exclude, - excludeLintJson: exclude, - excludeLintKnip: exclude, - excludeLintMd: exclude, - excludeLintPackageJson: exclude, - excludeLintPackages: exclude, - excludeLintPerfectionist: exclude, - excludeLintSpelling: exclude, - excludeLintYml: exclude, - excludeReleases: exclude, - excludeRenovate: exclude, - excludeTemplatedBy: exclude, - excludeTests: exclude, - mode: "create", - owner: "StubOwner", - repository: "stub-repository", - title: "Stub Title", - }) satisfies Options; - -describe("createDotGitHubFiles", () => { - it("creates only markdown files when given minimal options", async () => { - expect(await createDotGitHubFiles(createOptions(true))) - .toMatchInlineSnapshot(` - { - "CODE_OF_CONDUCT.md": "# Contributor Covenant Code of Conduct - - ## Our Pledge - - We as members, contributors, and leaders pledge to make participation in our - community a harassment-free experience for everyone, regardless of age, body - size, visible or invisible disability, ethnicity, sex characteristics, gender - identity and expression, level of experience, education, socio-economic status, - nationality, personal appearance, race, caste, color, religion, or sexual - identity and orientation. - - We pledge to act and interact in ways that contribute to an open, welcoming, - diverse, inclusive, and healthy community. - - ## Our Standards - - Examples of behavior that contributes to a positive environment for our - community include: - - - Demonstrating empathy and kindness toward other people - - Being respectful of differing opinions, viewpoints, and experiences - - Giving and gracefully accepting constructive feedback - - Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience - - Focusing on what is best not just for us as individuals, but for the overall - community - - Examples of unacceptable behavior include: - - - The use of sexualized language or imagery, and sexual attention or advances of - any kind - - Trolling, insulting or derogatory comments, and personal or political attacks - - Public or private harassment - - Publishing others' private information, such as a physical or email address, - without their explicit permission - - Other conduct which could reasonably be considered inappropriate in a - professional setting - - ## Enforcement Responsibilities - - Community leaders are responsible for clarifying and enforcing our standards of - acceptable behavior and will take appropriate and fair corrective action in - response to any behavior that they deem inappropriate, threatening, offensive, - or harmful. - - Community leaders have the right and responsibility to remove, edit, or reject - comments, commits, code, wiki edits, issues, and other contributions that are - not aligned to this Code of Conduct, and will communicate reasons for moderation - decisions when appropriate. - - ## Scope - - This Code of Conduct applies within all community spaces, and also applies when - an individual is officially representing the community in public spaces. - Examples of representing our community include using an official e-mail address, - posting via an official social media account, or acting as an appointed - representative at an online or offline event. - - ## Enforcement - - Instances of abusive, harassing, or otherwise unacceptable behavior may be - reported to the community leaders responsible for enforcement at - github@email.com. - All complaints will be reviewed and investigated promptly and fairly. - - All community leaders are obligated to respect the privacy and security of the - reporter of any incident. - - ## Enforcement Guidelines - - Community leaders will follow these Community Impact Guidelines in determining - the consequences for any action they deem in violation of this Code of Conduct: - - ### 1. Correction - - **Community Impact**: Use of inappropriate language or other behavior deemed - unprofessional or unwelcome in the community. - - **Consequence**: A private, written warning from community leaders, providing - clarity around the nature of the violation and an explanation of why the - behavior was inappropriate. A public apology may be requested. - - ### 2. Warning - - **Community Impact**: A violation through a single incident or series of - actions. - - **Consequence**: A warning with consequences for continued behavior. No - interaction with the people involved, including unsolicited interaction with - those enforcing the Code of Conduct, for a specified period of time. This - includes avoiding interactions in community spaces as well as external channels - like social media. Violating these terms may lead to a temporary or permanent - ban. - - ### 3. Temporary Ban - - **Community Impact**: A serious violation of community standards, including - sustained inappropriate behavior. - - **Consequence**: A temporary ban from any sort of interaction or public - communication with the community for a specified period of time. No public or - private interaction with the people involved, including unsolicited interaction - with those enforcing the Code of Conduct, is allowed during this period. - Violating these terms may lead to a permanent ban. - - ### 4. Permanent Ban - - **Community Impact**: Demonstrating a pattern of violation of community - standards, including sustained inappropriate behavior, harassment of an - individual, or aggression toward or disparagement of classes of individuals. - - **Consequence**: A permanent ban from any sort of public interaction within the - community. - - ## Attribution - - This Code of Conduct is adapted from the [Contributor Covenant][homepage], - version 2.1, available at - [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. - - Community Impact Guidelines were inspired by - [Mozilla's code of conduct enforcement ladder][mozilla coc]. - - For answers to common questions about this code of conduct, see the FAQ at - [https://www.contributor-covenant.org/faq][faq]. Translations are available at - [https://www.contributor-covenant.org/translations][translations]. - - [homepage]: https://www.contributor-covenant.org - [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html - [mozilla coc]: https://github.com/mozilla/diversity - [faq]: https://www.contributor-covenant.org/faq - [translations]: https://www.contributor-covenant.org/translations - ", - "CONTRIBUTING.md": "# Contributing - - Thanks for your interest in contributing to \`stub-repository\`! 💖 - - > After this page, see [DEVELOPMENT.md](./DEVELOPMENT.md) for local development instructions. - - ## Code of Conduct - - This project contains a [Contributor Covenant code of conduct](./CODE_OF_CONDUCT.md) all contributors are expected to follow. - - ## Reporting Issues - - Please do [report an issue on the issue tracker](https://github.com/StubOwner/stub-repository/issues/new/choose) if there's any bugfix, documentation improvement, or general enhancement you'd like to see in the repository! Please fully fill out all required fields in the most appropriate issue form. - - ## Sending Contributions - - Sending your own changes as contribution is always appreciated! - There are two steps involved: - - 1. [Finding an Issue](#finding-an-issue) - 2. [Sending a Pull Request](#sending-a-pull-request) - - ### Finding an Issue - - With the exception of very small typos, all changes to this repository generally need to correspond to an [unassigned open issue marked as \`status: accepting prs\` on the issue tracker](https://github.com/StubOwner/stub-repository/issues?q=is%3Aissue+is%3Aopen+label%3A%22status%3A+accepting+prs%22+no%3Aassignee+). - If this is your first time contributing, consider searching for [unassigned issues that also have the \`good first issue\` label](https://github.com/StubOwner/stub-repository/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22+label%3A%22status%3A+accepting+prs%22+no%3Aassignee+). - If the issue you'd like to fix isn't found on the issue, see [Reporting Issues](#reporting-issues) for filing your own (please do!). - - #### Issue Claiming - - We don't use any kind of issue claiming system. - We've found in the past that they result in accidental ["licked cookie"](https://devblogs.microsoft.com/oldnewthing/20091201-00/?p=15843) situations where contributors claim an issue but run out of time or energy trying before sending a PR. - - If an unassigned issue has been marked as \`status: accepting prs\` and an open PR does not exist, feel free to send a PR. - Please don't post comments asking for permission or stating you will work on an issue. - - ### Sending a Pull Request - - Once you've identified an open issue accepting PRs that doesn't yet have a PR sent, you're free to send a pull request. - Be sure to fill out the pull request template's requested information -- otherwise your PR will likely be closed. - - PRs are also expected to have a title that adheres to [conventional commits](https://www.conventionalcommits.org/en/v1.0.0). - Only PR titles need to be in that format, not individual commits. - Don't worry if you get this wrong: you can always change the PR title after sending it. - Check [previously merged PRs](https://github.com/StubOwner/stub-repository/pulls?q=is%3Apr+is%3Amerged+-label%3Adependencies+) for reference. - - #### Draft PRs - - If you don't think your PR is ready for review, [set it as a draft](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-stage-of-a-pull-request#converting-a-pull-request-to-a-draft). - Draft PRs won't be reviewed. - - #### Granular PRs - - Please keep pull requests single-purpose: in other words, don't attempt to solve multiple unrelated problems in one pull request. - Send one PR per area of concern. - Multi-purpose pull requests are harder and slower to review, block all changes from being merged until the whole pull request is reviewed, and are difficult to name well with semantic PR titles. - - #### Pull Request Reviews - - When a PR is not in draft, it's considered ready for review. - Please don't manually \`@\` tag anybody to request review. - A maintainer will look at it when they're next able to. - - PRs should have passing [GitHub status checks](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/about-status-checks) before review is requested (unless there are explicit questions asked in the PR about any failures). - - #### Asking Questions - - If you need help and/or have a question, posting a comment in the PR is a great way to do so. - There's no need to tag anybody individually. - One of us will drop by and help when we can. - - Please post comments as [line comments](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/commenting-on-a-pull-request#adding-line-comments-to-a-pull-request) when possible, so that they can be threaded. - You can [resolve conversations](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/commenting-on-a-pull-request#resolving-conversations) on your own when you feel they're resolved - no need to comment explicitly and/or wait for a maintainer. - - #### Requested Changes - - After a maintainer reviews your PR, they may request changes on it. - Once you've made those changes, [re-request review on GitHub](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/about-pull-request-reviews#re-requesting-a-review). - - Please try not to force-push commits to PRs that have already been reviewed. - Doing so makes it harder to review the changes. - We squash merge all commits so there's no need to try to preserve Git history within a PR branch. - - Once you've addressed all our feedback by making code changes and/or started a followup discussion, [re-request review](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/about-pull-request-reviews#re-requesting-a-review) from each maintainer whose feedback you addressed. - - Once all feedback is addressed and the PR is approved, we'll ensure the branch is up to date with \`main\` and merge it for you. - - #### Post-Merge Recognition - - Once your PR is merged, if you haven't yet been added to the [_Contributors_ table in the README.md](../README.md#contributors) for its [type of contribution](https://allcontributors.org/docs/en/emoji-key "Allcontributors emoji key"), you should be soon. - Please do ping the maintainer who merged your PR if that doesn't happen within 24 hours - it was likely an oversight on our end! - - ## Emojis & Appreciation - - If you made it all the way to the end, bravo dear user, we love you. - Please include your favorite emoji in the bottom of your issues and PRs to signal to us that you did in fact read this file and are trying to conform to it as best as possible. - 💖 is a good starter if you're not sure which to use. - ", - "DEVELOPMENT.md": "# Development!", - "ISSUE_TEMPLATE.md": " - - - - - - ## Overview - - ... - ", - "PULL_REQUEST_TEMPLATE.md": " - - ## PR Checklist - - - [ ] Addresses an existing open issue: fixes #000 - - [ ] That issue was marked as [\`status: accepting prs\`](https://github.com/StubOwner/stub-repository/issues?q=is%3Aopen+is%3Aissue+label%3A%22status%3A+accepting+prs%22) - - [ ] Steps in [CONTRIBUTING.md](https://github.com/StubOwner/stub-repository/blob/main/.github/CONTRIBUTING.md) were taken - - ## Overview - - - ", - "SECURITY.md": "# Security Policy - - We take all security vulnerabilities seriously. - If you have a vulnerability or other security issues to disclose: - - - Thank you very much, please do! - - Please send them to us by emailing \`github@email.com\` - - We appreciate your efforts and responsible disclosure and will make every effort to acknowledge your contributions. - ", - } - `); - }); - - it("creates markdown and extra files when given full options", async () => { - expect(await createDotGitHubFiles(createOptions(false))) - .toMatchInlineSnapshot(` - { - "CODE_OF_CONDUCT.md": "# Contributor Covenant Code of Conduct - - ## Our Pledge - - We as members, contributors, and leaders pledge to make participation in our - community a harassment-free experience for everyone, regardless of age, body - size, visible or invisible disability, ethnicity, sex characteristics, gender - identity and expression, level of experience, education, socio-economic status, - nationality, personal appearance, race, caste, color, religion, or sexual - identity and orientation. - - We pledge to act and interact in ways that contribute to an open, welcoming, - diverse, inclusive, and healthy community. - - ## Our Standards - - Examples of behavior that contributes to a positive environment for our - community include: - - - Demonstrating empathy and kindness toward other people - - Being respectful of differing opinions, viewpoints, and experiences - - Giving and gracefully accepting constructive feedback - - Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience - - Focusing on what is best not just for us as individuals, but for the overall - community - - Examples of unacceptable behavior include: - - - The use of sexualized language or imagery, and sexual attention or advances of - any kind - - Trolling, insulting or derogatory comments, and personal or political attacks - - Public or private harassment - - Publishing others' private information, such as a physical or email address, - without their explicit permission - - Other conduct which could reasonably be considered inappropriate in a - professional setting - - ## Enforcement Responsibilities - - Community leaders are responsible for clarifying and enforcing our standards of - acceptable behavior and will take appropriate and fair corrective action in - response to any behavior that they deem inappropriate, threatening, offensive, - or harmful. - - Community leaders have the right and responsibility to remove, edit, or reject - comments, commits, code, wiki edits, issues, and other contributions that are - not aligned to this Code of Conduct, and will communicate reasons for moderation - decisions when appropriate. - - ## Scope - - This Code of Conduct applies within all community spaces, and also applies when - an individual is officially representing the community in public spaces. - Examples of representing our community include using an official e-mail address, - posting via an official social media account, or acting as an appointed - representative at an online or offline event. - - ## Enforcement - - Instances of abusive, harassing, or otherwise unacceptable behavior may be - reported to the community leaders responsible for enforcement at - github@email.com. - All complaints will be reviewed and investigated promptly and fairly. - - All community leaders are obligated to respect the privacy and security of the - reporter of any incident. - - ## Enforcement Guidelines - - Community leaders will follow these Community Impact Guidelines in determining - the consequences for any action they deem in violation of this Code of Conduct: - - ### 1. Correction - - **Community Impact**: Use of inappropriate language or other behavior deemed - unprofessional or unwelcome in the community. - - **Consequence**: A private, written warning from community leaders, providing - clarity around the nature of the violation and an explanation of why the - behavior was inappropriate. A public apology may be requested. - - ### 2. Warning - - **Community Impact**: A violation through a single incident or series of - actions. - - **Consequence**: A warning with consequences for continued behavior. No - interaction with the people involved, including unsolicited interaction with - those enforcing the Code of Conduct, for a specified period of time. This - includes avoiding interactions in community spaces as well as external channels - like social media. Violating these terms may lead to a temporary or permanent - ban. - - ### 3. Temporary Ban - - **Community Impact**: A serious violation of community standards, including - sustained inappropriate behavior. - - **Consequence**: A temporary ban from any sort of interaction or public - communication with the community for a specified period of time. No public or - private interaction with the people involved, including unsolicited interaction - with those enforcing the Code of Conduct, is allowed during this period. - Violating these terms may lead to a permanent ban. - - ### 4. Permanent Ban - - **Community Impact**: Demonstrating a pattern of violation of community - standards, including sustained inappropriate behavior, harassment of an - individual, or aggression toward or disparagement of classes of individuals. - - **Consequence**: A permanent ban from any sort of public interaction within the - community. - - ## Attribution - - This Code of Conduct is adapted from the [Contributor Covenant][homepage], - version 2.1, available at - [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. - - Community Impact Guidelines were inspired by - [Mozilla's code of conduct enforcement ladder][mozilla coc]. - - For answers to common questions about this code of conduct, see the FAQ at - [https://www.contributor-covenant.org/faq][faq]. Translations are available at - [https://www.contributor-covenant.org/translations][translations]. - - [homepage]: https://www.contributor-covenant.org - [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html - [mozilla coc]: https://github.com/mozilla/diversity - [faq]: https://www.contributor-covenant.org/faq - [translations]: https://www.contributor-covenant.org/translations - ", - "CONTRIBUTING.md": "# Contributing - - Thanks for your interest in contributing to \`stub-repository\`! 💖 - - > After this page, see [DEVELOPMENT.md](./DEVELOPMENT.md) for local development instructions. - - ## Code of Conduct - - This project contains a [Contributor Covenant code of conduct](./CODE_OF_CONDUCT.md) all contributors are expected to follow. - - ## Reporting Issues - - Please do [report an issue on the issue tracker](https://github.com/StubOwner/stub-repository/issues/new/choose) if there's any bugfix, documentation improvement, or general enhancement you'd like to see in the repository! Please fully fill out all required fields in the most appropriate issue form. - - ## Sending Contributions - - Sending your own changes as contribution is always appreciated! - There are two steps involved: - - 1. [Finding an Issue](#finding-an-issue) - 2. [Sending a Pull Request](#sending-a-pull-request) - - ### Finding an Issue - - With the exception of very small typos, all changes to this repository generally need to correspond to an [unassigned open issue marked as \`status: accepting prs\` on the issue tracker](https://github.com/StubOwner/stub-repository/issues?q=is%3Aissue+is%3Aopen+label%3A%22status%3A+accepting+prs%22+no%3Aassignee+). - If this is your first time contributing, consider searching for [unassigned issues that also have the \`good first issue\` label](https://github.com/StubOwner/stub-repository/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22+label%3A%22status%3A+accepting+prs%22+no%3Aassignee+). - If the issue you'd like to fix isn't found on the issue, see [Reporting Issues](#reporting-issues) for filing your own (please do!). - - #### Issue Claiming - - We don't use any kind of issue claiming system. - We've found in the past that they result in accidental ["licked cookie"](https://devblogs.microsoft.com/oldnewthing/20091201-00/?p=15843) situations where contributors claim an issue but run out of time or energy trying before sending a PR. - - If an unassigned issue has been marked as \`status: accepting prs\` and an open PR does not exist, feel free to send a PR. - Please don't post comments asking for permission or stating you will work on an issue. - - ### Sending a Pull Request - - Once you've identified an open issue accepting PRs that doesn't yet have a PR sent, you're free to send a pull request. - Be sure to fill out the pull request template's requested information -- otherwise your PR will likely be closed. - - PRs are also expected to have a title that adheres to [conventional commits](https://www.conventionalcommits.org/en/v1.0.0). - Only PR titles need to be in that format, not individual commits. - Don't worry if you get this wrong: you can always change the PR title after sending it. - Check [previously merged PRs](https://github.com/StubOwner/stub-repository/pulls?q=is%3Apr+is%3Amerged+-label%3Adependencies+) for reference. - - #### Draft PRs - - If you don't think your PR is ready for review, [set it as a draft](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-stage-of-a-pull-request#converting-a-pull-request-to-a-draft). - Draft PRs won't be reviewed. - - #### Granular PRs - - Please keep pull requests single-purpose: in other words, don't attempt to solve multiple unrelated problems in one pull request. - Send one PR per area of concern. - Multi-purpose pull requests are harder and slower to review, block all changes from being merged until the whole pull request is reviewed, and are difficult to name well with semantic PR titles. - - #### Pull Request Reviews - - When a PR is not in draft, it's considered ready for review. - Please don't manually \`@\` tag anybody to request review. - A maintainer will look at it when they're next able to. - - PRs should have passing [GitHub status checks](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/about-status-checks) before review is requested (unless there are explicit questions asked in the PR about any failures). - - #### Asking Questions - - If you need help and/or have a question, posting a comment in the PR is a great way to do so. - There's no need to tag anybody individually. - One of us will drop by and help when we can. - - Please post comments as [line comments](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/commenting-on-a-pull-request#adding-line-comments-to-a-pull-request) when possible, so that they can be threaded. - You can [resolve conversations](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/commenting-on-a-pull-request#resolving-conversations) on your own when you feel they're resolved - no need to comment explicitly and/or wait for a maintainer. - - #### Requested Changes - - After a maintainer reviews your PR, they may request changes on it. - Once you've made those changes, [re-request review on GitHub](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/about-pull-request-reviews#re-requesting-a-review). - - Please try not to force-push commits to PRs that have already been reviewed. - Doing so makes it harder to review the changes. - We squash merge all commits so there's no need to try to preserve Git history within a PR branch. - - Once you've addressed all our feedback by making code changes and/or started a followup discussion, [re-request review](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/about-pull-request-reviews#re-requesting-a-review) from each maintainer whose feedback you addressed. - - Once all feedback is addressed and the PR is approved, we'll ensure the branch is up to date with \`main\` and merge it for you. - - #### Post-Merge Recognition - - Once your PR is merged, if you haven't yet been added to the [_Contributors_ table in the README.md](../README.md#contributors) for its [type of contribution](https://allcontributors.org/docs/en/emoji-key "Allcontributors emoji key"), you should be soon. - Please do ping the maintainer who merged your PR if that doesn't happen within 24 hours - it was likely an oversight on our end! - - ## Emojis & Appreciation - - If you made it all the way to the end, bravo dear user, we love you. - Please include your favorite emoji in the bottom of your issues and PRs to signal to us that you did in fact read this file and are trying to conform to it as best as possible. - 💖 is a good starter if you're not sure which to use. - ", - "DEVELOPMENT.md": "# Development!", - "ISSUE_TEMPLATE.md": " - - - - - - ## Overview - - ... - ", - "PULL_REQUEST_TEMPLATE.md": " - - ## PR Checklist - - - [ ] Addresses an existing open issue: fixes #000 - - [ ] That issue was marked as [\`status: accepting prs\`](https://github.com/StubOwner/stub-repository/issues?q=is%3Aopen+is%3Aissue+label%3A%22status%3A+accepting+prs%22) - - [ ] Steps in [CONTRIBUTING.md](https://github.com/StubOwner/stub-repository/blob/main/.github/CONTRIBUTING.md) were taken - - ## Overview - - - ", - "SECURITY.md": "# Security Policy - - We take all security vulnerabilities seriously. - If you have a vulnerability or other security issues to disclose: - - - Thank you very much, please do! - - Please send them to us by emailing \`github@email.com\` - - We appreciate your efforts and responsible disclosure and will make every effort to acknowledge your contributions. - ", - "renovate.json": "{ - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "automerge": true, - "extends": ["config:best-practices", "replacements:all"], - "ignoreDeps": ["codecov/codecov-action"], - "labels": ["dependencies"], - "minimumReleaseAge": "7 days", - "patch": { "enabled": false }, - "postUpdateOptions": ["pnpmDedupe"] - } - ", - } - `); - }); -}); diff --git a/src/steps/writing/creation/dotGitHub/createDotGitHubFiles.ts b/src/steps/writing/creation/dotGitHub/createDotGitHubFiles.ts deleted file mode 100644 index 0443d1ced..000000000 --- a/src/steps/writing/creation/dotGitHub/createDotGitHubFiles.ts +++ /dev/null @@ -1,292 +0,0 @@ -/* spellchecker: disable */ -import { Options } from "../../../../shared/types.js"; -import { formatJson } from "../formatters/formatJson.js"; -import { formatYaml } from "../formatters/formatYaml.js"; -import { createDevelopment } from "./createDevelopment/index.js"; - -export async function createDotGitHubFiles(options: Options) { - return { - "CODE_OF_CONDUCT.md": `# Contributor Covenant Code of Conduct - -## Our Pledge - -We as members, contributors, and leaders pledge to make participation in our -community a harassment-free experience for everyone, regardless of age, body -size, visible or invisible disability, ethnicity, sex characteristics, gender -identity and expression, level of experience, education, socio-economic status, -nationality, personal appearance, race, caste, color, religion, or sexual -identity and orientation. - -We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community. - -## Our Standards - -Examples of behavior that contributes to a positive environment for our -community include: - -- Demonstrating empathy and kindness toward other people -- Being respectful of differing opinions, viewpoints, and experiences -- Giving and gracefully accepting constructive feedback -- Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience -- Focusing on what is best not just for us as individuals, but for the overall - community - -Examples of unacceptable behavior include: - -- The use of sexualized language or imagery, and sexual attention or advances of - any kind -- Trolling, insulting or derogatory comments, and personal or political attacks -- Public or private harassment -- Publishing others' private information, such as a physical or email address, - without their explicit permission -- Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Enforcement Responsibilities - -Community leaders are responsible for clarifying and enforcing our standards of -acceptable behavior and will take appropriate and fair corrective action in -response to any behavior that they deem inappropriate, threatening, offensive, -or harmful. - -Community leaders have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are -not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate. - -## Scope - -This Code of Conduct applies within all community spaces, and also applies when -an individual is officially representing the community in public spaces. -Examples of representing our community include using an official e-mail address, -posting via an official social media account, or acting as an appointed -representative at an online or offline event. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement at -${options.email.github}. -All complaints will be reviewed and investigated promptly and fairly. - -All community leaders are obligated to respect the privacy and security of the -reporter of any incident. - -## Enforcement Guidelines - -Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct: - -### 1. Correction - -**Community Impact**: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community. - -**Consequence**: A private, written warning from community leaders, providing -clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested. - -### 2. Warning - -**Community Impact**: A violation through a single incident or series of -actions. - -**Consequence**: A warning with consequences for continued behavior. No -interaction with the people involved, including unsolicited interaction with -those enforcing the Code of Conduct, for a specified period of time. This -includes avoiding interactions in community spaces as well as external channels -like social media. Violating these terms may lead to a temporary or permanent -ban. - -### 3. Temporary Ban - -**Community Impact**: A serious violation of community standards, including -sustained inappropriate behavior. - -**Consequence**: A temporary ban from any sort of interaction or public -communication with the community for a specified period of time. No public or -private interaction with the people involved, including unsolicited interaction -with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban. - -### 4. Permanent Ban - -**Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals. - -**Consequence**: A permanent ban from any sort of public interaction within the -community. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 2.1, available at -[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. - -Community Impact Guidelines were inspired by -[Mozilla's code of conduct enforcement ladder][mozilla coc]. - -For answers to common questions about this code of conduct, see the FAQ at -[https://www.contributor-covenant.org/faq][faq]. Translations are available at -[https://www.contributor-covenant.org/translations][translations]. - -[homepage]: https://www.contributor-covenant.org -[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html -[mozilla coc]: https://github.com/mozilla/diversity -[faq]: https://www.contributor-covenant.org/faq -[translations]: https://www.contributor-covenant.org/translations -`, - "CONTRIBUTING.md": `# Contributing - -Thanks for your interest in contributing to \`${options.repository}\`! 💖 - -> After this page, see [DEVELOPMENT.md](./DEVELOPMENT.md) for local development instructions. - -## Code of Conduct - -This project contains a [Contributor Covenant code of conduct](./CODE_OF_CONDUCT.md) all contributors are expected to follow. - -## Reporting Issues - -Please do [report an issue on the issue tracker](https://github.com/${options.owner}/${options.repository}/issues/new/choose) if there's any bugfix, documentation improvement, or general enhancement you'd like to see in the repository! Please fully fill out all required fields in the most appropriate issue form. - -## Sending Contributions - -Sending your own changes as contribution is always appreciated! -There are two steps involved: - -1. [Finding an Issue](#finding-an-issue) -2. [Sending a Pull Request](#sending-a-pull-request) - -### Finding an Issue - -With the exception of very small typos, all changes to this repository generally need to correspond to an [unassigned open issue marked as \`status: accepting prs\` on the issue tracker](https://github.com/${options.owner}/${options.repository}/issues?q=is%3Aissue+is%3Aopen+label%3A%22status%3A+accepting+prs%22+no%3Aassignee+). -If this is your first time contributing, consider searching for [unassigned issues that also have the \`good first issue\` label](https://github.com/${options.owner}/${options.repository}/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22+label%3A%22status%3A+accepting+prs%22+no%3Aassignee+). -If the issue you'd like to fix isn't found on the issue, see [Reporting Issues](#reporting-issues) for filing your own (please do!). - -#### Issue Claiming - -We don't use any kind of issue claiming system. -We've found in the past that they result in accidental ["licked cookie"](https://devblogs.microsoft.com/oldnewthing/20091201-00/?p=15843) situations where contributors claim an issue but run out of time or energy trying before sending a PR. - -If an unassigned issue has been marked as \`status: accepting prs\` and an open PR does not exist, feel free to send a PR. -Please don't post comments asking for permission or stating you will work on an issue. - -### Sending a Pull Request - -Once you've identified an open issue accepting PRs that doesn't yet have a PR sent, you're free to send a pull request. -Be sure to fill out the pull request template's requested information -- otherwise your PR will likely be closed. - -PRs are also expected to have a title that adheres to [conventional commits](https://www.conventionalcommits.org/en/v1.0.0). -Only PR titles need to be in that format, not individual commits. -Don't worry if you get this wrong: you can always change the PR title after sending it. -Check [previously merged PRs](https://github.com/${options.owner}/${options.repository}/pulls?q=is%3Apr+is%3Amerged+-label%3Adependencies+) for reference. - -#### Draft PRs - -If you don't think your PR is ready for review, [set it as a draft](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-stage-of-a-pull-request#converting-a-pull-request-to-a-draft). -Draft PRs won't be reviewed. - -#### Granular PRs - -Please keep pull requests single-purpose: in other words, don't attempt to solve multiple unrelated problems in one pull request. -Send one PR per area of concern. -Multi-purpose pull requests are harder and slower to review, block all changes from being merged until the whole pull request is reviewed, and are difficult to name well with semantic PR titles. - -#### Pull Request Reviews - -When a PR is not in draft, it's considered ready for review. -Please don't manually \`@\` tag anybody to request review. -A maintainer will look at it when they're next able to. - -PRs should have passing [GitHub status checks](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/about-status-checks) before review is requested (unless there are explicit questions asked in the PR about any failures). - -#### Asking Questions - -If you need help and/or have a question, posting a comment in the PR is a great way to do so. -There's no need to tag anybody individually. -One of us will drop by and help when we can. - -Please post comments as [line comments](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/commenting-on-a-pull-request#adding-line-comments-to-a-pull-request) when possible, so that they can be threaded. -You can [resolve conversations](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/commenting-on-a-pull-request#resolving-conversations) on your own when you feel they're resolved - no need to comment explicitly and/or wait for a maintainer. - -#### Requested Changes - -After a maintainer reviews your PR, they may request changes on it. -Once you've made those changes, [re-request review on GitHub](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/about-pull-request-reviews#re-requesting-a-review). - -Please try not to force-push commits to PRs that have already been reviewed. -Doing so makes it harder to review the changes. -We squash merge all commits so there's no need to try to preserve Git history within a PR branch. - -Once you've addressed all our feedback by making code changes and/or started a followup discussion, [re-request review](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/about-pull-request-reviews#re-requesting-a-review) from each maintainer whose feedback you addressed. - -Once all feedback is addressed and the PR is approved, we'll ensure the branch is up to date with \`main\` and merge it for you. - -#### Post-Merge Recognition - -Once your PR is merged, if you haven't yet been added to the [_Contributors_ table in the README.md](../README.md#contributors) for its [type of contribution](https://allcontributors.org/docs/en/emoji-key "Allcontributors emoji key"), you should be soon. -Please do ping the maintainer who merged your PR if that doesn't happen within 24 hours - it was likely an oversight on our end! - -## Emojis & Appreciation - -If you made it all the way to the end, bravo dear user, we love you. -Please include your favorite emoji in the bottom of your issues and PRs to signal to us that you did in fact read this file and are trying to conform to it as best as possible. -💖 is a good starter if you're not sure which to use. -`, - "DEVELOPMENT.md": await createDevelopment(options), - ...(options.funding && { - "FUNDING.yml": formatYaml({ github: options.funding }), - }), - - "ISSUE_TEMPLATE.md": ` - - - - - -## Overview - -... -`, - "PULL_REQUEST_TEMPLATE.md": ` - -## PR Checklist - -- [ ] Addresses an existing open issue: fixes #000 -- [ ] That issue was marked as [\`status: accepting prs\`](https://github.com/${options.owner}/${options.repository}/issues?q=is%3Aopen+is%3Aissue+label%3A%22status%3A+accepting+prs%22) -- [ ] Steps in [CONTRIBUTING.md](https://github.com/${options.owner}/${options.repository}/blob/main/.github/CONTRIBUTING.md) were taken - -## Overview - - -`, - "SECURITY.md": `# Security Policy - -We take all security vulnerabilities seriously. -If you have a vulnerability or other security issues to disclose: - -- Thank you very much, please do! -- Please send them to us by emailing \`${options.email.github}\` - -We appreciate your efforts and responsible disclosure and will make every effort to acknowledge your contributions. -`, - ...(!options.excludeRenovate && { - "renovate.json": await formatJson({ - $schema: "https://docs.renovatebot.com/renovate-schema.json", - automerge: true, - extends: ["config:best-practices", "replacements:all"], - ignoreDeps: ["codecov/codecov-action"], - labels: ["dependencies"], - minimumReleaseAge: "7 days", - patch: { enabled: false }, - postUpdateOptions: ["pnpmDedupe"], - }), - }), - }; -} diff --git a/src/steps/writing/creation/dotGitHub/createMultiWorkflowFile.test.ts b/src/steps/writing/creation/dotGitHub/createMultiWorkflowFile.test.ts deleted file mode 100644 index 36fd4e318..000000000 --- a/src/steps/writing/creation/dotGitHub/createMultiWorkflowFile.test.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { describe, expect, it } from "vitest"; - -import { createMultiWorkflowFile } from "./createMultiWorkflowFile.js"; - -describe("createMultiWorkflowFile", () => { - it("creates a workflow file when runs are provided", () => { - const actual = createMultiWorkflowFile({ - jobs: [ - { - name: "Job A", - steps: [{ run: "task-a" }], - }, - { - name: "Job B", - steps: [{ uses: "task-b" }], - }, - ], - name: "Test Name", - }); - - expect(actual).toMatchInlineSnapshot(` - "jobs: - job_a: - name: Job A - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/prepare - - run: task-a - job_b: - name: Job B - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/prepare - - uses: task-b - - name: Test Name - - on: - pull_request: ~ - push: - branches: - - main - " - `); - }); -}); diff --git a/src/steps/writing/creation/dotGitHub/createSoloWorkflowFile.test.ts b/src/steps/writing/creation/dotGitHub/createSoloWorkflowFile.test.ts deleted file mode 100644 index e6e7261f7..000000000 --- a/src/steps/writing/creation/dotGitHub/createSoloWorkflowFile.test.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { describe, expect, it } from "vitest"; - -import { createSoloWorkflowFile } from "./createSoloWorkflowFile.js"; - -describe("createSoloWorkflowFile", () => { - it("creates a workflow file when runs are provided", () => { - const actual = createSoloWorkflowFile({ - name: "Test Name", - runs: ["pnpm build"], - }); - - expect(actual).toMatchInlineSnapshot(` - "jobs: - test_name: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/prepare - - run: pnpm build - - name: Test Name - " - `); - }); -}); diff --git a/src/steps/writing/creation/dotGitHub/createWorkflows.test.ts b/src/steps/writing/creation/dotGitHub/createWorkflows.test.ts deleted file mode 100644 index 00bfb4298..000000000 --- a/src/steps/writing/creation/dotGitHub/createWorkflows.test.ts +++ /dev/null @@ -1,378 +0,0 @@ -import { describe, expect, it } from "vitest"; - -import { Options } from "../../../../shared/types.js"; -import { createWorkflows } from "./createWorkflows.js"; - -const createOptions = (exclude: boolean) => - ({ - access: "public", - base: "everything", - bin: exclude ? undefined : "./bin/index.js", - description: "Test description.", - directory: ".", - email: { - github: "github@email.com", - npm: "npm@email.com", - }, - excludeAllContributors: exclude, - excludeCompliance: exclude, - excludeLintJson: exclude, - excludeLintKnip: exclude, - excludeLintMd: exclude, - excludeLintPackageJson: exclude, - excludeLintPackages: exclude, - excludeLintPerfectionist: exclude, - excludeLintSpelling: exclude, - excludeLintYml: exclude, - excludeReleases: exclude, - excludeRenovate: exclude, - excludeTemplatedBy: exclude, - excludeTests: exclude, - mode: "create", - owner: "StubOwner", - repository: "stub-repository", - title: "Stub Title", - }) satisfies Options; - -describe("createWorkflows", () => { - it("creates a full set of workflows when all excludes are disabled", () => { - const workflows = createWorkflows(createOptions(false)); - - expect(workflows).toMatchInlineSnapshot(` - { - "accessibility-alt-text-bot.yml": "jobs: - accessibility_alt_text_bot: - if: \${{ !endsWith(github.actor, '[bot]') }} - runs-on: ubuntu-latest - steps: - - uses: github/accessibility-alt-text-bot@v1.4.0 - - name: Accessibility Alt Text Bot - - on: - issue_comment: - types: - - created - - edited - issues: - types: - - edited - - opened - pull_request: - types: - - edited - - opened - - permissions: - issues: write - pull-requests: write - ", - "ci.yml": "jobs: - build: - name: Build - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/prepare - - run: pnpm build - - run: node ./lib/index.js - lint: - name: Lint - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/prepare - - run: pnpm build - - run: pnpm lint - lint_knip: - name: Lint Knip - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/prepare - - run: pnpm lint:knip - lint_markdown: - name: Lint Markdown - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/prepare - - run: pnpm lint:md - lint_packages: - name: Lint Packages - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/prepare - - run: pnpm lint:packages - lint_spelling: - name: Lint Spelling - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/prepare - - run: pnpm lint:spelling - prettier: - name: Prettier - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/prepare - - run: pnpm format --list-different - test: - name: Test - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/prepare - - run: pnpm run test --coverage - - if: always() - uses: codecov/codecov-action@v3 - type_check: - name: Type Check - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/prepare - - run: pnpm tsc - - name: CI - - on: - pull_request: ~ - push: - branches: - - main - ", - "compliance.yml": "jobs: - compliance: - runs-on: ubuntu-latest - steps: - - uses: mtfoley/pr-compliance-action@main - with: - body-auto-close: false - ignore-authors: |- - allcontributors - allcontributors[bot] - renovate - renovate[bot] - ignore-team-members: false - - name: Compliance - - on: - pull_request: - branches: - - main - types: - - edited - - opened - - reopened - - synchronize - - permissions: - pull-requests: write - ", - "contributors.yml": "jobs: - contributors: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - uses: ./.github/actions/prepare - - env: - GITHUB_TOKEN: \${{ secrets.ACCESS_TOKEN }} - uses: JoshuaKGoldberg/all-contributors-auto-action@v0.5.0 - - name: Contributors - - on: - push: - branches: - - main - ", - "post-release.yml": "jobs: - post_release: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - run: echo "npm_version=$(npm pkg get version | tr -d '"')" >> "$GITHUB_ENV" - - uses: apexskier/github-release-commenter@v1 - with: - GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }} - comment-template: | - :tada: This is included in version {release_link} :tada: - - The release is available on: - - * [GitHub releases](https://github.com/StubOwner/stub-repository/releases/tag/{release_tag}) - * [npm package (@latest dist-tag)](https://www.npmjs.com/package/stub-repository/v/\${{ env.npm_version }}) - - Cheers! 📦🚀 - - name: Post Release - - on: - release: - types: - - published - - permissions: - issues: write - pull-requests: write - ", - "pr-review-requested.yml": "jobs: - pr_review_requested: - runs-on: ubuntu-latest - steps: - - uses: actions-ecosystem/action-remove-labels@v1 - with: - labels: "status: waiting for author" - - if: failure() - run: | - echo "Don't worry if the previous step failed." - echo "See https://github.com/actions-ecosystem/action-remove-labels/issues/221." - - name: PR Review Requested - - on: - pull_request_target: - types: - - review_requested - - permissions: - pull-requests: write - ", - "release.yml": "concurrency: - group: \${{ github.workflow }} - - jobs: - release: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - ref: main - - uses: ./.github/actions/prepare - - run: pnpm build - - env: - GITHUB_TOKEN: \${{ secrets.ACCESS_TOKEN }} - NPM_TOKEN: \${{ secrets.NPM_TOKEN }} - uses: JoshuaKGoldberg/release-it-action@v0.2.2 - - name: Release - - on: - push: - branches: - - main - - permissions: - contents: write - id-token: write - ", - } - `); - }); - - it("creates a minimal set of workflows when all options are enabled", () => { - const workflows = createWorkflows(createOptions(true)); - - expect(workflows).toMatchInlineSnapshot(` - { - "accessibility-alt-text-bot.yml": "jobs: - accessibility_alt_text_bot: - if: \${{ !endsWith(github.actor, '[bot]') }} - runs-on: ubuntu-latest - steps: - - uses: github/accessibility-alt-text-bot@v1.4.0 - - name: Accessibility Alt Text Bot - - on: - issue_comment: - types: - - created - - edited - issues: - types: - - edited - - opened - pull_request: - types: - - edited - - opened - - permissions: - issues: write - pull-requests: write - ", - "ci.yml": "jobs: - build: - name: Build - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/prepare - - run: pnpm build - - run: node ./lib/index.js - lint: - name: Lint - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/prepare - - run: pnpm lint - prettier: - name: Prettier - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/prepare - - run: pnpm format --list-different - type_check: - name: Type Check - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/prepare - - run: pnpm tsc - - name: CI - - on: - pull_request: ~ - push: - branches: - - main - ", - "pr-review-requested.yml": "jobs: - pr_review_requested: - runs-on: ubuntu-latest - steps: - - uses: actions-ecosystem/action-remove-labels@v1 - with: - labels: "status: waiting for author" - - if: failure() - run: | - echo "Don't worry if the previous step failed." - echo "See https://github.com/actions-ecosystem/action-remove-labels/issues/221." - - name: PR Review Requested - - on: - pull_request_target: - types: - - review_requested - - permissions: - pull-requests: write - ", - } - `); - }); -}); diff --git a/src/steps/writing/creation/dotGitHub/createWorkflows.ts b/src/steps/writing/creation/dotGitHub/createWorkflows.ts deleted file mode 100644 index f6d67a632..000000000 --- a/src/steps/writing/creation/dotGitHub/createWorkflows.ts +++ /dev/null @@ -1,247 +0,0 @@ -import { Options } from "../../../../shared/types.js"; -import { createMultiWorkflowFile } from "./createMultiWorkflowFile.js"; -import { createSoloWorkflowFile } from "./createSoloWorkflowFile.js"; - -export function createWorkflows(options: Options) { - return { - "accessibility-alt-text-bot.yml": createSoloWorkflowFile({ - if: "${{ !endsWith(github.actor, '[bot]') }}", - name: "Accessibility Alt Text Bot", - on: { - issue_comment: { - types: ["created", "edited"], - }, - issues: { - types: ["edited", "opened"], - }, - pull_request: { - types: ["edited", "opened"], - }, - }, - permissions: { - issues: "write", - "pull-requests": "write", - }, - steps: [ - { - uses: "github/accessibility-alt-text-bot@v1.4.0", - }, - ], - }), - "ci.yml": createMultiWorkflowFile({ - jobs: [ - { - name: "Build", - steps: [{ run: "pnpm build" }, { run: "node ./lib/index.js" }], - }, - { - name: "Prettier", - steps: [{ run: "pnpm format --list-different" }], - }, - { - name: "Type Check", - steps: [{ run: "pnpm tsc" }], - }, - { - name: "Lint", - steps: [ - ...(options.bin ? [{ run: "pnpm build" }] : []), - { run: "pnpm lint" }, - ], - }, - ...(options.excludeLintKnip - ? [] - : [ - { - name: "Lint Knip", - steps: [{ run: "pnpm lint:knip" }], - }, - ]), - ...(options.excludeLintMd - ? [] - : [ - { - name: "Lint Markdown", - steps: [{ run: "pnpm lint:md" }], - }, - ]), - ...(options.excludeLintPackages - ? [] - : [ - { - name: "Lint Packages", - steps: [{ run: "pnpm lint:packages" }], - }, - ]), - ...(options.excludeLintSpelling - ? [] - : [ - { - name: "Lint Spelling", - steps: [{ run: "pnpm lint:spelling" }], - }, - ]), - ...(options.excludeTests - ? [] - : [ - { - name: "Test", - steps: [ - { run: "pnpm run test --coverage" }, - { if: "always()", uses: "codecov/codecov-action@v3" }, - ], - }, - ]), - ], - name: "CI", - }), - ...(!options.excludeCompliance && { - "compliance.yml": createSoloWorkflowFile({ - name: "Compliance", - on: { - pull_request: { - branches: ["main"], - types: ["edited", "opened", "reopened", "synchronize"], - }, - }, - permissions: { - "pull-requests": "write", - }, - steps: [ - { - uses: "mtfoley/pr-compliance-action@main", - with: { - "body-auto-close": false, - "ignore-authors": - [ - ...(options.excludeAllContributors - ? [] - : ["allcontributors", "allcontributors[bot]"]), - ...(options.excludeRenovate - ? [] - : ["renovate", "renovate[bot]"]), - ].join("\n") || undefined, - "ignore-team-members": false, - }, - }, - ], - }), - }), - ...(!options.excludeAllContributors && { - "contributors.yml": createSoloWorkflowFile({ - name: "Contributors", - on: { - push: { - branches: ["main"], - }, - }, - steps: [ - { uses: "actions/checkout@v4", with: { "fetch-depth": 0 } }, - { uses: "./.github/actions/prepare" }, - { - env: { GITHUB_TOKEN: "${{ secrets.ACCESS_TOKEN }}" }, - uses: `JoshuaKGoldberg/all-contributors-auto-action@v0.5.0`, - }, - ], - }), - }), - ...(!options.excludeReleases && { - "post-release.yml": createSoloWorkflowFile({ - name: "Post Release", - on: { - release: { - types: ["published"], - }, - }, - permissions: { - issues: "write", - "pull-requests": "write", - }, - steps: [ - { uses: "actions/checkout@v4", with: { "fetch-depth": 0 } }, - { - run: `echo "npm_version=$(npm pkg get version | tr -d '"')" >> "$GITHUB_ENV"`, - }, - { - uses: "apexskier/github-release-commenter@v1", - with: { - "comment-template": ` - :tada: This is included in version {release_link} :tada: - - The release is available on: - - * [GitHub releases](https://github.com/${options.owner}/${options.repository}/releases/tag/{release_tag}) - * [npm package (@latest dist-tag)](https://www.npmjs.com/package/${options.repository}/v/\${{ env.npm_version }}) - - Cheers! 📦🚀 - `, - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}", - }, - }, - ], - }), - }), - "pr-review-requested.yml": createSoloWorkflowFile({ - name: "PR Review Requested", - on: { - pull_request_target: { - types: ["review_requested"], - }, - }, - permissions: { - "pull-requests": "write", - }, - steps: [ - { - uses: "actions-ecosystem/action-remove-labels@v1", - with: { - labels: "status: waiting for author", - }, - }, - { - if: "failure()", - run: 'echo "Don\'t worry if the previous step failed."\necho "See https://github.com/actions-ecosystem/action-remove-labels/issues/221."\n', - }, - ], - }), - ...(!options.excludeReleases && { - "release.yml": createSoloWorkflowFile({ - concurrency: { - group: "${{ github.workflow }}", - }, - name: "Release", - on: { - push: { - branches: ["main"], - }, - }, - permissions: { - contents: "write", - "id-token": "write", - }, - steps: [ - { - uses: "actions/checkout@v4", - with: { - "fetch-depth": 0, - ref: "main", - }, - }, - { - uses: "./.github/actions/prepare", - }, - { - run: "pnpm build", - }, - { - env: { - GITHUB_TOKEN: "${{ secrets.ACCESS_TOKEN }}", - NPM_TOKEN: "${{ secrets.NPM_TOKEN }}", - }, - uses: "JoshuaKGoldberg/release-it-action@v0.2.2", - }, - ], - }), - }), - }; -} diff --git a/src/steps/writing/creation/dotGitHub/index.ts b/src/steps/writing/creation/dotGitHub/index.ts deleted file mode 100644 index 031d1b827..000000000 --- a/src/steps/writing/creation/dotGitHub/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Options } from "../../../../shared/types.js"; -import { createDotGitHubActions } from "./actions.js"; -import { createDotGitHubFiles } from "./createDotGitHubFiles.js"; -import { createWorkflows } from "./createWorkflows.js"; -import { createDotGitHubIssueTemplate } from "./issueTemplate.js"; - -export async function createDotGitHub(options: Options) { - return { - actions: createDotGitHubActions(), - ISSUE_TEMPLATE: createDotGitHubIssueTemplate(options), - workflows: createWorkflows(options), - ...(await createDotGitHubFiles(options)), - }; -} diff --git a/src/steps/writing/creation/dotGitHub/issueTemplate.ts b/src/steps/writing/creation/dotGitHub/issueTemplate.ts deleted file mode 100644 index 314240185..000000000 --- a/src/steps/writing/creation/dotGitHub/issueTemplate.ts +++ /dev/null @@ -1,206 +0,0 @@ -import { Options } from "../../../../shared/types.js"; -import { formatYaml } from "../formatters/formatYaml.js"; - -export function createDotGitHubIssueTemplate({ - owner, - repository, -}: Pick) { - return { - "01-bug.yml": formatYaml({ - body: [ - { - attributes: { - description: - "If any of these required steps are not taken, we may not be able to review your issue. Help us to help you!", - label: "Bug Report Checklist", - options: [ - { - label: "I have tried restarting my IDE and the issue persists.", - required: true, - }, - { - label: - "I have pulled the latest `main` branch of the repository.", - required: true, - }, - { - label: `I have [searched for related issues](https://github.com/${owner}/${repository}/issues?q=is%3Aissue) and found none that matched my issue.`, - required: true, - }, - ], - }, - type: "checkboxes", - }, - { - attributes: { - description: "What did you expect to happen?", - label: "Expected", - }, - type: "textarea", - validations: { - required: true, - }, - }, - { - attributes: { - description: "What happened instead?", - label: "Actual", - }, - type: "textarea", - validations: { - required: true, - }, - }, - { - attributes: { - description: "Any additional info you'd like to provide.", - label: "Additional Info", - }, - type: "textarea", - }, - ], - description: "Report a bug trying to run the code", - labels: ["type: bug"], - name: "🐛 Bug", - title: "🐛 Bug: ", - }), - "02-documentation.yml": formatYaml({ - body: [ - { - attributes: { - description: - "If any of these required steps are not taken, we may not be able to review your issue. Help us to help you!", - label: "Bug Report Checklist", - options: [ - { - label: - "I have pulled the latest `main` branch of the repository.", - required: true, - }, - { - label: `I have [searched for related issues](https://github.com/${owner}/${repository}/issues?q=is%3Aissue) and found none that matched my issue.`, - required: true, - }, - ], - }, - type: "checkboxes", - }, - { - attributes: { - description: "What would you like to report?", - label: "Overview", - }, - type: "textarea", - validations: { - required: true, - }, - }, - { - attributes: { - description: "Any additional info you'd like to provide.", - label: "Additional Info", - }, - type: "textarea", - }, - ], - description: "Report a typo or missing area of documentation", - labels: ["area: documentation"], - name: "📝 Documentation", - title: "📝 Documentation: ", - }), - "03-feature.yml": formatYaml({ - body: [ - { - attributes: { - description: - "If any of these required steps are not taken, we may not be able to review your issue. Help us to help you!", - label: "Bug Report Checklist", - options: [ - { - label: - "I have pulled the latest `main` branch of the repository.", - required: true, - }, - { - label: `I have [searched for related issues](https://github.com/${owner}/${repository}/issues?q=is%3Aissue) and found none that matched my issue.`, - required: true, - }, - ], - }, - type: "checkboxes", - }, - { - attributes: { - description: "What did you expect to be able to do?", - label: "Overview", - }, - type: "textarea", - validations: { - required: true, - }, - }, - { - attributes: { - description: "Any additional info you'd like to provide.", - label: "Additional Info", - }, - type: "textarea", - }, - ], - description: - "Request that a new feature be added or an existing feature improved", - labels: ["type: feature"], - name: "🚀 Feature", - title: "🚀 Feature: ", - }), - "04-tooling.yml": formatYaml({ - body: [ - { - attributes: { - description: - "If any of these required steps are not taken, we may not be able to review your issue. Help us to help you!", - label: "Bug Report Checklist", - options: [ - { - label: "I have tried restarting my IDE and the issue persists.", - required: true, - }, - { - label: - "I have pulled the latest `main` branch of the repository.", - required: true, - }, - { - label: `I have [searched for related issues](https://github.com/${owner}/${repository}/issues?q=is%3Aissue) and found none that matched my issue.`, - required: true, - }, - ], - }, - type: "checkboxes", - }, - { - attributes: { - description: "What did you expect to be able to do?", - label: "Overview", - }, - type: "textarea", - validations: { - required: true, - }, - }, - { - attributes: { - description: "Any additional info you'd like to provide.", - label: "Additional Info", - }, - type: "textarea", - }, - ], - description: - "Report a bug or request an enhancement in repository tooling", - labels: ["area: tooling"], - name: "🛠 Tooling", - title: "🛠 Tooling: ", - }), - }; -} diff --git a/src/steps/writing/creation/dotHusky.ts b/src/steps/writing/creation/dotHusky.ts deleted file mode 100644 index 8de1e458a..000000000 --- a/src/steps/writing/creation/dotHusky.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { formatIgnoreFile } from "./formatters/formatIgnoreFile.js"; - -export function createDotHusky() { - return { - ".gitignore": formatIgnoreFile(["_"]), - "pre-commit": formatIgnoreFile(["npx lint-staged"]), - }; -} diff --git a/src/steps/writing/creation/dotVSCode.test.ts b/src/steps/writing/creation/dotVSCode.test.ts deleted file mode 100644 index 482f1b591..000000000 --- a/src/steps/writing/creation/dotVSCode.test.ts +++ /dev/null @@ -1,290 +0,0 @@ -import { describe, expect, it } from "vitest"; - -import { Options } from "../../../shared/types.js"; -import { createDotVSCode } from "./dotVSCode.js"; - -/* spellchecker: disable */ -function fakeOptions( - getExcludeValue: (exclusionName: string) => boolean, - bin?: string, -) { - return { - access: "public", - author: "TestAuthor", - base: "everything", - ...(bin ? { bin } : {}), - description: "Test description.", - directory: ".", - email: { - github: "github@email.com", - npm: "npm@email.com", - }, - ...Object.fromEntries( - [ - "excludeCompliance", - "excludeAllContributors", - "excludeLintESLint", - "excludeLintJSDoc", - "excludeLintJson", - "excludeLintKnip", - "excludeLintMd", - "excludeLintPackageJson", - "excludeLintPackages", - "excludeLintPerfectionist", - "excludeLintRegexp", - "excludeLintSpelling", - "excludeLintStrict", - "excludeLintStylistic", - "excludeLintYml", - "excludeReleases", - "excludeRenovate", - "excludeTests", - ].map((key) => [key, getExcludeValue(key)]), - ), - mode: "create", - owner: "TestOwner", - repository: "test-repository", - skipGitHubApi: true, - skipInstall: true, - skipRemoval: true, - title: "Test Title", - } satisfies Options; -} - -describe("createDotVSCode", () => { - it("creates a minimal config when all exclusions are enabled", async () => { - expect(await createDotVSCode(fakeOptions(() => true))) - .toMatchInlineSnapshot(` - { - "extensions.json": "{ "recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"] } - ", - "settings.json": "{ - "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" }, - "editor.defaultFormatter": "esbenp.prettier-vscode", - "editor.formatOnSave": true, - "editor.rulers": [80], - "eslint.probe": [ - "javascript", - "javascriptreact", - "json", - "jsonc", - "markdown", - "typescript", - "typescriptreact", - "yaml" - ], - "eslint.rules.customizations": [{ "rule": "*", "severity": "warn" }], - "typescript.tsdk": "node_modules/typescript/lib" - } - ", - "tasks.json": "{ - "tasks": [ - { - "detail": "Build the project", - "label": "build", - "script": "build", - "type": "npm" - } - ], - "version": "2.0.0" - } - ", - } - `); - }); - - it("creates a full config when all exclusions are disabled and bin is provided", async () => { - expect(await createDotVSCode(fakeOptions(() => false, "bin/index.js"))) - .toMatchInlineSnapshot(` - { - "extensions.json": "{ - "recommendations": [ - "DavidAnson.vscode-markdownlint", - "dbaeumer.vscode-eslint", - "esbenp.prettier-vscode", - "streetsidesoftware.code-spell-checker", - "vitest.explorer" - ] - } - ", - "launch.json": "{ - "configurations": [ - { - "args": ["run", "\${relativeFile}"], - "autoAttachChildProcesses": true, - "console": "integratedTerminal", - "name": "Debug Current Test File", - "program": "\${workspaceRoot}/node_modules/vitest/vitest.mjs", - "request": "launch", - "skipFiles": ["/**", "**/node_modules/**"], - "smartStep": true, - "type": "node" - }, - { - "name": "Debug Program", - "preLaunchTask": "build", - "program": "bin/index.js", - "request": "launch", - "skipFiles": ["/**"], - "type": "node" - } - ], - "version": "0.2.0" - } - ", - "settings.json": "{ - "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" }, - "editor.defaultFormatter": "esbenp.prettier-vscode", - "editor.formatOnSave": true, - "editor.rulers": [80], - "eslint.probe": [ - "javascript", - "javascriptreact", - "json", - "jsonc", - "markdown", - "typescript", - "typescriptreact", - "yaml" - ], - "eslint.rules.customizations": [{ "rule": "*", "severity": "warn" }], - "typescript.tsdk": "node_modules/typescript/lib" - } - ", - "tasks.json": "{ - "tasks": [ - { - "detail": "Build the project", - "label": "build", - "script": "build", - "type": "npm" - } - ], - "version": "2.0.0" - } - ", - } - `); - }); - - it("creates a full config when all exclusions are disabled and bin is not provided", async () => { - expect(await createDotVSCode(fakeOptions(() => false))) - .toMatchInlineSnapshot(` - { - "extensions.json": "{ - "recommendations": [ - "DavidAnson.vscode-markdownlint", - "dbaeumer.vscode-eslint", - "esbenp.prettier-vscode", - "streetsidesoftware.code-spell-checker", - "vitest.explorer" - ] - } - ", - "launch.json": "{ - "configurations": [ - { - "args": ["run", "\${relativeFile}"], - "autoAttachChildProcesses": true, - "console": "integratedTerminal", - "name": "Debug Current Test File", - "program": "\${workspaceRoot}/node_modules/vitest/vitest.mjs", - "request": "launch", - "skipFiles": ["/**", "**/node_modules/**"], - "smartStep": true, - "type": "node" - } - ], - "version": "0.2.0" - } - ", - "settings.json": "{ - "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" }, - "editor.defaultFormatter": "esbenp.prettier-vscode", - "editor.formatOnSave": true, - "editor.rulers": [80], - "eslint.probe": [ - "javascript", - "javascriptreact", - "json", - "jsonc", - "markdown", - "typescript", - "typescriptreact", - "yaml" - ], - "eslint.rules.customizations": [{ "rule": "*", "severity": "warn" }], - "typescript.tsdk": "node_modules/typescript/lib" - } - ", - "tasks.json": "{ - "tasks": [ - { - "detail": "Build the project", - "label": "build", - "script": "build", - "type": "npm" - } - ], - "version": "2.0.0" - } - ", - } - `); - }); - - it("creates a minimal config including launch.json when all exclusions are enabled and bin is provided", async () => { - expect(await createDotVSCode(fakeOptions(() => true, "bin/index.js"))) - .toMatchInlineSnapshot(` - { - "extensions.json": "{ "recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"] } - ", - "launch.json": "{ - "configurations": [ - { - "name": "Debug Program", - "preLaunchTask": "build", - "program": "bin/index.js", - "request": "launch", - "skipFiles": ["/**"], - "type": "node" - } - ], - "version": "0.2.0" - } - ", - "settings.json": "{ - "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" }, - "editor.defaultFormatter": "esbenp.prettier-vscode", - "editor.formatOnSave": true, - "editor.rulers": [80], - "eslint.probe": [ - "javascript", - "javascriptreact", - "json", - "jsonc", - "markdown", - "typescript", - "typescriptreact", - "yaml" - ], - "eslint.rules.customizations": [{ "rule": "*", "severity": "warn" }], - "typescript.tsdk": "node_modules/typescript/lib" - } - ", - "tasks.json": "{ - "tasks": [ - { - "detail": "Build the project", - "label": "build", - "script": "build", - "type": "npm" - } - ], - "version": "2.0.0" - } - ", - } - `); - }); -}); diff --git a/src/steps/writing/creation/dotVSCode.ts b/src/steps/writing/creation/dotVSCode.ts deleted file mode 100644 index 6ed9f2ff2..000000000 --- a/src/steps/writing/creation/dotVSCode.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { Options } from "../../../shared/types.js"; -import { formatJson } from "./formatters/formatJson.js"; - -/* spellchecker: disable */ -export async function createDotVSCode(options: Options) { - return { - "extensions.json": await formatJson({ - recommendations: [ - !options.excludeLintMd && "DavidAnson.vscode-markdownlint", - "dbaeumer.vscode-eslint", - "esbenp.prettier-vscode", - !options.excludeLintSpelling && "streetsidesoftware.code-spell-checker", - !options.excludeTests && "vitest.explorer", - ].filter(Boolean), - }), - ...(options.excludeTests && !options.bin - ? {} - : { - "launch.json": await formatJson({ - configurations: [ - ...(options.excludeTests - ? [] - : [ - { - args: ["run", "${relativeFile}"], - autoAttachChildProcesses: true, - console: "integratedTerminal", - name: "Debug Current Test File", - program: - "${workspaceRoot}/node_modules/vitest/vitest.mjs", - request: "launch", - skipFiles: ["/**", "**/node_modules/**"], - smartStep: true, - type: "node", - }, - ]), - ...(options.bin - ? [ - { - name: "Debug Program", - preLaunchTask: "build", - program: options.bin, - request: "launch", - skipFiles: ["/**"], - type: "node", - }, - ] - : []), - ], - version: "0.2.0", - }), - }), - "settings.json": await formatJson({ - "editor.codeActionsOnSave": { - "source.fixAll.eslint": "explicit", - }, - "editor.defaultFormatter": "esbenp.prettier-vscode", - "editor.formatOnSave": true, - "editor.rulers": [80], - "eslint.probe": [ - "javascript", - "javascriptreact", - "json", - "jsonc", - "markdown", - "typescript", - "typescriptreact", - "yaml", - ], - "eslint.rules.customizations": [{ rule: "*", severity: "warn" }], - "typescript.tsdk": "node_modules/typescript/lib", - }), - "tasks.json": await formatJson({ - tasks: [ - { - detail: "Build the project", - label: "build", - script: "build", - type: "npm", - }, - ], - version: "2.0.0", - }), - }; -} diff --git a/src/steps/writing/creation/formatters/formatJson.test.ts b/src/steps/writing/creation/formatters/formatJson.test.ts deleted file mode 100644 index 719dd7296..000000000 --- a/src/steps/writing/creation/formatters/formatJson.test.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { describe, expect, it } from "vitest"; - -import { formatJson } from "./formatJson.js"; - -describe("formatJson", () => { - it("removes undefined values", async () => { - const actual = await formatJson({ empty: undefined, exists: true }); - - expect(actual).toMatchInlineSnapshot(` - "{ "exists": true } - " - `); - }); -}); diff --git a/src/steps/writing/creation/formatters/formatJson.ts b/src/steps/writing/creation/formatters/formatJson.ts deleted file mode 100644 index e35f8eb27..000000000 --- a/src/steps/writing/creation/formatters/formatJson.ts +++ /dev/null @@ -1,15 +0,0 @@ -import prettier from "prettier"; - -export async function formatJson(value: object) { - return await prettier.format( - JSON.stringify( - Object.fromEntries( - Object.entries(value).filter((entry) => entry[1] !== undefined), - ), - ), - { - parser: "json", - useTabs: true, - }, - ); -} diff --git a/src/steps/writing/creation/formatters/formatTypeScript.ts b/src/steps/writing/creation/formatters/formatTypeScript.ts deleted file mode 100644 index 0c9c72bc2..000000000 --- a/src/steps/writing/creation/formatters/formatTypeScript.ts +++ /dev/null @@ -1,5 +0,0 @@ -import prettier from "prettier"; - -export async function formatTypeScript(value: string) { - return await prettier.format(value, { parser: "typescript", useTabs: true }); -} diff --git a/src/steps/writing/creation/index.test.ts b/src/steps/writing/creation/index.test.ts deleted file mode 100644 index 5dd7e9b2b..000000000 --- a/src/steps/writing/creation/index.test.ts +++ /dev/null @@ -1,311 +0,0 @@ -import { Octokit } from "octokit"; -import { describe, expect, it, vi } from "vitest"; - -import { getPackageDependencies } from "../../../next/blocks/packageData.js"; -import { Options } from "../../../shared/types.js"; -import { createStructure } from "./index.js"; - -vi.mock("../../../next/utils/resolveBin.ts", () => ({ - resolveBin: (bin: string) => `path/to/${bin}`, -})); - -/* eslint-disable @typescript-eslint/no-dynamic-delete */ - -const documentation = ` -## Setup Scripts - -As described in the \`README.md\` file and \`docs/\`, this template repository comes with three scripts that can set up an existing or new repository. - -Each follows roughly the same general flow: - -1. \`bin/index.ts\` uses \`bin/mode.ts\` to determine which of the three setup scripts to run -2. \`readOptions\` parses in options from local files, Git commands, npm APIs, and/or files on disk -3. \`runOrRestore\` wraps the setup script's main logic in a friendly prompt wrapper -4. The setup script wraps each portion of its main logic with \`withSpinner\` - - Each step of setup logic is generally imported from within \`src/steps\` -5. A call to \`outro\` summarizes the results for the user - -> **Warning** -> Each setup script overrides many files in the directory they're run in. -> Make sure to save any changes you want to preserve before running them. - -### The Creation Script - -> 📝 See [\`docs/Creation.md\`](../docs/Creation.md) for user documentation on the creation script. - -This template's "creation" script is located in \`src/create/\`. -You can run it locally with \`node bin/index.js --mode create\`. -Note that files need to be built with \`pnpm run build\` beforehand. - -#### Testing the Creation Script - -You can run the end-to-end test for creation locally on the command-line. -Note that the files need to be built with \`pnpm run build\` beforehand. - -\`\`\`shell -pnpm run test:create -\`\`\` - -That end-to-end test executes \`script/create-test-e2e.ts\`, which: - -1. Runs the creation script to create a new \`test-repository\` child directory and repository, capturing code coverage -2. Asserts that commands such as \`build\` and \`lint\` each pass - -The \`pnpm run test:create\` script is run in CI to ensure that templating changes are in sync with the template's actual files. -See \`.github/workflows/ci.yml\`'s \`test_creation_script\` job. - -### The Initialization Script - -> 📝 See [\`docs/Initialization.md\`](../docs/Initialization.md) for user documentation on the initialization script. - -This template's "initialization" script is located in \`src/initialize/\`. -You can run it locally with \`pnpm run initialize\`. -It uses [\`tsx\`](https://github.com/esbuild-kit/tsx) so you don't need to build files before running. - -\`\`\`shell -pnpm run initialize -\`\`\` - -#### Testing the Initialization Script - -You can run the end-to-end test for initializing locally on the command-line. -Note that files need to be built with \`pnpm run build\` beforehand. - -\`\`\`shell -pnpm run test:initialize -\`\`\` - -That end-to-end test executes \`script/initialize-test-e2e.ts\`, which: - -1. Runs the initialization script using \`--skip-github-api\` and other skip flags -2. Checks that the local repository's files were changed correctly (e.g. removed initialization-only files) -3. Runs \`pnpm run lint:knip\` to make sure no excess dependencies or files were left over -4. Resets everything -5. Runs initialization a second time, capturing test coverage - -The \`pnpm run test:initialize\` script is run in CI to ensure that templating changes are in sync with the template's actual files. -See \`.github/workflows/ci.yml\`'s \`test_initialization_script\` job. - -### The Migration Script - -> 📝 See [\`docs/Migration.md\`](../docs/Migration.md) for user documentation on the migration script. - -This template's "migration" script is located in \`src/migrate/\`. -Note that files need to be built with \`pnpm run build\` beforehand. - -To test out the script locally, run it from a different repository's directory: - -\`\`\`shell -cd ../other-repo -node ../create-typescript-app/bin/migrate.js -\`\`\` - -The migration script will work on any directory. -You can try it out in a blank directory with scripts like: - -\`\`\`shell -cd .. -mkdir temp -cd temp -node ../create-typescript-app/bin/migrate.js -\`\`\` - -#### Testing the Migration Script - -> 💡 Seeing \`Oh no! Running the migrate script unexpectedly modified:\` errors? -> _[Unexpected File Modifications](#unexpected-file-modifications)_ covers that below. - -You can run the end-to-end test for migrating locally on the command-line: - -\`\`\`shell -pnpm run test:migrate -\`\`\` - -That end-to-end test executes \`script/migrate-test-e2e.ts\`, which: - -1. Runs the migration script using \`--skip-github-api\` and other skip flags, capturing code coverage -2. Checks that only a small list of allowed files were changed -3. Checks that the local repository's files were changed correctly (e.g. removed initialization-only files) - -The \`pnpm run test:migrate\` script is run in CI to ensure that templating changes are in sync with the template's actual files. -See \`.github/workflows/ci.yml\`'s \`test_migration_script\` job. - -> Tip: if the migration test is failing in CI and you don't see any errors, try [downloading the full logs](https://docs.github.com/en/actions/monitoring-and-troubleshooting-workflows/using-workflow-run-logs#downloading-logs). - -##### Migration Snapshot Failures - -The migration test uses the [Vitest file snapshot](https://vitest.dev/guide/snapshot#file-snapshots) in \`script/__snapshots__/migrate-test-e2e.ts.snap\` to store expected differences to this repository after running the migration script. -The end-to-end migration test will fail any changes that don't keep the same differences in that snapshot. - -You can update the snapshot file by: - -1. Committing any changes to your local repository -2. Running \`pnpm i\` and \`pnpm build\` if any updates have been made to the \`package.json\` or \`src/\` files, respectively -3. Running \`pnpm run test:migrate -u\` to update the snapshot - -At this point there will be some files changed: - -- \`script/__snapshots__/migrate-test-e2e.ts.snap\` will have updates if any files mismatched templates -- The actual updated files on disk will be there too - -If the snapshot file changes are what you expected, then you can commit them. -The rest of the file changes can be reverted. - -> [🚀 Feature: Add a way to apply known file changes after migration #1184](https://github.com/JoshuaKGoldberg/create-typescript-app/issues/1184) tracks turning the test snapshot into a feature. - -##### Unexpected File Modifications - -The migration test also asserts that no files were unexpectedly changed. -If you see a failure like: - -\`\`\`plaintext -Oh no! Running the migrate script unexpectedly modified: - - ... -\`\`\` - -...then that means the file generated from templates differs from what's checked into the repository. -This is most often caused by changes to templates not being applied to checked-in files too. - -Templates for files are generally stored in [\`src/steps/writing/creation\`] under a path roughly corresponding to the file they describe. -For example, the template for \`tsup.config.ts\` is stored in [\`src/steps/writing/creation/createTsupConfig.ts\`](../src/steps/writing/creation/createTsupConfig.ts). -If the \`createTsupConfig\` function were to be modified without an equivalent change to \`tsup.config.ts\` -or vice-versa- then the migration test would report: - -\`\`\`plaintext -Oh no! Running the migrate script unexpectedly modified: - - tsup.config.ts -\`\`\` -`; - -const optionsBaseline: Options = { - access: "public", - author: "Test Author", - base: "everything", - bin: "bin/test.js", - description: "Test Description", - directory: "test-directory", - email: { github: "github@example.com", npm: "npm@example.com" }, - funding: "Test Funding", - guide: { - href: "https://www.joshuakgoldberg.com/blog/contributing-to-a-create-typescript-app-repository", - title: "Contributing to a create-typescript-app Repository", - }, - keywords: ["test", "keywords"], - logo: { alt: "Test Alt", src: "test.png" }, - mode: "create", - owner: "Test Owner", - repository: "test-repo", - title: "Test Title", -}; - -describe("createStructure", () => { - describe.each(["minimal", "common", "everything"])("base %s", () => { - it("matches current and next", async () => { - const optionsNext = { - ...optionsBaseline, - documentation, - packageData: { - dependencies: getPackageDependencies( - "@clack/prompts", - "@prettier/sync", - "chalk", - "create", - "cspell-populate-words", - "execa", - "git-remote-origin-url", - "git-url-parse", - "html-to-text", - "image-size", - "input-from-file", - "input-from-file-json", - "input-from-script", - "js-yaml", - "lazy-value", - "npm-user", - "object-strings-deep", - "octokit", - "octokit-from-auth", - "parse-author", - "parse-package-name", - "populate-all-contributors-for-repository", - "prettier", - "remove-undefined-objects", - "replace-in-file", - "rimraf", - "set-github-repository-labels", - "sort-package-json", - "title-case", - "zod", - "zod-validation-error", - ), - devDependencies: getPackageDependencies( - "@octokit/request-error", - "@release-it/conventional-changelog", - "@types/git-url-parse", - "@types/html-to-text", - "@types/js-yaml", - "@types/parse-author", - "all-contributors-cli", - "c8", - "create-testers", - "cspell", - "globby", - "release-it", - "tsx", - ), - scripts: { - initialize: - "pnpm build --no-dts && tsx ./bin/index.js --mode initialize", - "test:create": "npx tsx script/create-test-e2e.ts", - "test:initialize": "npx tsx script/initialize-test-e2e.ts", - "test:migrate": "vitest run -r script/", - }, - }, - }; - - const baseline = await createStructure(optionsBaseline, false); - const next = await createStructure(optionsNext, true, { - fetch: vi.fn(), - octokit: {} as Octokit, - }); - - // Test display cleaning: just don't show values that are the same - deleteEqualValuesDeep(baseline, next); - - for (const rootFile of [ - // This is created separately - "README.md", - ]) { - delete baseline[rootFile]; - delete next[rootFile]; - } - - expect(next).toEqual(baseline); - }); - }); -}); - -function deleteEqualValues(a: T, b: T) { - for (const i in a) { - if (a[i] === b[i]) { - delete b[i]; - delete a[i]; - } - } -} - -function deleteEqualValuesDeep(a: T, b: T) { - deleteEqualValues(a, b); - - for (const i in a) { - if (a[i] && typeof a[i] === "object" && b[i] && typeof b[i] === "object") { - deleteEqualValuesDeep(a[i], b[i]); - - if (Object.keys(a[i]).length === 0 && Object.keys(b[i]).length === 0) { - delete a[i]; - delete b[i]; - } - } - } -} - -/* eslint-enable @typescript-eslint/no-dynamic-delete */ diff --git a/src/steps/writing/creation/index.ts b/src/steps/writing/creation/index.ts deleted file mode 100644 index 72cb4db41..000000000 --- a/src/steps/writing/creation/index.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { - CreatedFiles, - produceBase, - producePreset, - SystemFetchers, -} from "create"; -import prettier from "prettier"; - -import { presetCommon } from "../../../next/presets/presetCommon.js"; -import { presetEverything } from "../../../next/presets/presetEverything.js"; -import { presetMinimal } from "../../../next/presets/presetMinimal.js"; -import { Options } from "../../../shared/types.js"; -import { Structure } from "../types.js"; -import { createDotGitHub } from "./dotGitHub/index.js"; -import { createDotHusky } from "./dotHusky.js"; -import { createDotVSCode } from "./dotVSCode.js"; -import { createRootFiles } from "./rootFiles.js"; -import { createSrc } from "./src.js"; - -const presets = { - common: presetCommon, - everything: presetEverything, - minimal: presetMinimal, -}; - -export async function createStructure( - providedOptions: Options, - useNextEngine: boolean, - fetchers?: SystemFetchers, -): Promise { - const preset = - useNextEngine && - providedOptions.base && - providedOptions.base !== "prompt" && - presets[providedOptions.base]; - - if (preset) { - const options = { - ...(await produceBase(preset.base, { - fetchers, - options: providedOptions, - })), - ...providedOptions, - }; - const creation = await producePreset(preset, { - fetchers, - mode: "initialize", - options, - }); - - return await recursivelyFormat(creation.files); - } - - return { - ".github": await createDotGitHub(providedOptions), - ".husky": createDotHusky(), - ".vscode": await createDotVSCode(providedOptions), - ...(providedOptions.mode !== "migrate" && { - src: await createSrc(providedOptions), - }), - ...(await createRootFiles(providedOptions)), - }; -} - -async function recursivelyFormat(files: CreatedFiles): Promise { - const result: Structure = {}; - - for (const [key, value] of Object.entries(files)) { - if (Array.isArray(value)) { - result[key] = await formatCreatedFile(key, value[0]); - } else if (typeof value === "string") { - result[key] = await formatCreatedFile(key, value); - } else if (typeof value === "object") { - result[key] = await recursivelyFormat(value); - } - } - - return result; -} - -const asYaml = new Set([ - ".gitignore", - ".markdownlintignore", - ".nvmrc", - ".prettierignore", - "pre-commit", -]); - -async function formatCreatedFile(filepath: string, entry: string) { - // For now, explicit yml files internally already have formatting applied - if (filepath.endsWith(".yml")) { - return entry; - } - - return await prettier.format(entry, { - useTabs: true, - ...(asYaml.has(filepath) ? { parser: "yaml" } : { filepath }), - }); -} diff --git a/src/steps/writing/creation/rootFiles.ts b/src/steps/writing/creation/rootFiles.ts deleted file mode 100644 index 0684468dc..000000000 --- a/src/steps/writing/creation/rootFiles.ts +++ /dev/null @@ -1,149 +0,0 @@ -import { Options } from "../../../shared/types.js"; -import { createCSpellConfig } from "./createCSpellConfig.js"; -import { createDotGitignore } from "./createDotGitignore.js"; -import { createESLintConfig } from "./createESLintConfig.js"; -import { createKnipConfig } from "./createKnipConfig.js"; -import { createTsupConfig } from "./createTsupConfig.js"; -import { formatIgnoreFile } from "./formatters/formatIgnoreFile.js"; -import { formatJson } from "./formatters/formatJson.js"; -import { writeAllContributorsRC } from "./writeAllContributorsRC.js"; -import { writePackageJson } from "./writePackageJson.js"; - -export async function createRootFiles(options: Options) { - return { - ".all-contributorsrc": await writeAllContributorsRC(options), - ".gitignore": createDotGitignore(options), - "eslint.config.js": await createESLintConfig(options), - ...(!options.excludeLintMd && { - ".markdownlint.json": await formatJson({ - extends: "markdownlint/style/prettier", - "first-line-h1": false, - "no-inline-html": false, - }), - ".markdownlintignore": formatIgnoreFile([ - ".github/CODE_OF_CONDUCT.md", - "CHANGELOG.md", - "lib/", - "node_modules/", - ]), - }), - ".nvmrc": `20.18.0\n`, - ".prettierignore": formatIgnoreFile([ - "/.husky", - ...(options.excludeTests ? [] : ["/coverage"]), - "/lib", - "/pnpm-lock.yaml", - ]), - ".prettierrc.json": await formatJson({ - $schema: "http://json.schemastore.org/prettierrc", - overrides: [ - { - files: ".nvmrc", - options: { parser: "yaml" }, - }, - ], - plugins: [ - "prettier-plugin-curly", - "prettier-plugin-packagejson", - "prettier-plugin-sh", - ], - useTabs: true, - }), - ...(!options.excludeReleases && { - ".release-it.json": await formatJson({ - git: { - commitMessage: "chore: release v${version}", - requireCommits: true, - }, - github: { - autoGenerate: true, - release: true, - releaseName: "v${version}", - }, - npm: { - publishArgs: [`--access ${options.access}`, "--provenance"], - }, - plugins: { - "@release-it/conventional-changelog": { - infile: "CHANGELOG.md", - preset: "angular", - types: [ - { section: "Features", type: "feat" }, - { section: "Bug Fixes", type: "fix" }, - { section: "Performance Improvements", type: "perf" }, - { hidden: true, type: "build" }, - { hidden: true, type: "chore" }, - { hidden: true, type: "ci" }, - { hidden: true, type: "docs" }, - { hidden: true, type: "refactor" }, - { hidden: true, type: "style" }, - { hidden: true, type: "test" }, - ], - }, - }, - }), - }), - "LICENSE.md": `# MIT License - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -'Software'), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -`, - ...(!options.excludeLintSpelling && { - "cspell.json": await createCSpellConfig(options), - }), - ...(!options.excludeLintKnip && { - "knip.json": await createKnipConfig(), - }), - "package.json": await writePackageJson(options), - "tsconfig.json": await formatJson({ - compilerOptions: { - declaration: true, - declarationMap: true, - esModuleInterop: true, - module: "NodeNext", - moduleResolution: "NodeNext", - noEmit: true, - resolveJsonModule: true, - skipLibCheck: true, - sourceMap: true, - strict: true, - target: "ES2022", - }, - include: ["src"], - }), - "tsup.config.ts": await createTsupConfig(options), - ...(!options.excludeTests && { - "vitest.config.ts": `import { defineConfig } from "vitest/config"; - -export default defineConfig({ - test: { - clearMocks: true, - coverage: { - all: true, - include: ["src"], - reporter: ["html", "lcov"], - }, - exclude: ["lib", "node_modules"], - setupFiles: ["console-fail-test/setup"], - }, -}); -`, - }), - }; -} diff --git a/src/steps/writing/creation/src.ts b/src/steps/writing/creation/src.ts deleted file mode 100644 index 895d44dfb..000000000 --- a/src/steps/writing/creation/src.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { Options } from "../../../shared/types.js"; -import { formatTypeScript } from "./formatters/formatTypeScript.js"; - -export async function createSrc(options: Options) { - return { - ...(!options.excludeTests && { - "greet.test.ts": await formatTypeScript( - ` - import { describe, expect, it, vi } from "vitest"; - - import { greet } from "./greet.js"; - - const message = "Yay, testing!"; - - describe("greet", () => { - it("logs to the console once when message is provided as a string", () => { - const logger = vi.spyOn(console, "log").mockImplementation(() => undefined); - - greet(message); - - expect(logger).toHaveBeenCalledWith(message); - expect(logger).toHaveBeenCalledTimes(1); - }); - - it("logs to the console once when message is provided as an object", () => { - const logger = vi.spyOn(console, "log").mockImplementation(() => undefined); - - greet({ message }); - - expect(logger).toHaveBeenCalledWith(message); - expect(logger).toHaveBeenCalledTimes(1); - }); - - it("logs once when times is not provided in an object", () => { - const logger = vi.fn(); - - greet({ logger, message }); - - expect(logger).toHaveBeenCalledWith(message); - expect(logger).toHaveBeenCalledTimes(1); - }); - - it("logs a specified number of times when times is provided", () => { - const logger = vi.fn(); - const times = 7; - - greet({ logger, message, times }); - - expect(logger).toHaveBeenCalledWith(message); - expect(logger).toHaveBeenCalledTimes(7); - }); - }); - `, - ), - }), - "greet.ts": await formatTypeScript( - `import { GreetOptions } from "./types.js"; - - export function greet(options: GreetOptions | string) { - const { - logger = console.log.bind(console), - message, - times = 1, - } = typeof options === "string" ? { message: options } : options; - - for (let i = 0; i < times; i += 1) { - logger(message); - } - } - `, - ), - "index.ts": await formatTypeScript( - ` - export * from "./greet.js"; - export * from "./types.js"; - `, - ), - "types.ts": await formatTypeScript( - ` - export interface GreetOptions { - logger?: (message: string) => void; - message: string; - times?: number; - } - `, - ), - }; -} diff --git a/src/steps/writing/creation/writeAllContributorsRC.ts b/src/steps/writing/creation/writeAllContributorsRC.ts deleted file mode 100644 index 9af327a99..000000000 --- a/src/steps/writing/creation/writeAllContributorsRC.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { readFileSafeAsJson } from "../../../shared/readFileSafeAsJson.js"; -import { AllContributorsData, Options } from "../../../shared/types.js"; -import { formatJson } from "./formatters/formatJson.js"; - -export async function writeAllContributorsRC(options: Options) { - const existing = (await readFileSafeAsJson( - ".all-contributorsrc", - )) as AllContributorsData | null; - - return await formatJson({ - badgeTemplate: - ' 👪 All Contributors: <%= contributors.length %>', - contributors: existing?.contributors ?? [], - contributorsSortAlphabetically: true, - projectName: options.repository, - projectOwner: options.owner, - }); -} diff --git a/src/steps/writing/creation/writePackageJson.test.ts b/src/steps/writing/creation/writePackageJson.test.ts deleted file mode 100644 index 69c197c9c..000000000 --- a/src/steps/writing/creation/writePackageJson.test.ts +++ /dev/null @@ -1,184 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; - -import { Options } from "../../../shared/types.js"; -import { writePackageJson } from "./writePackageJson.js"; - -const mockReadFileSafeAsJson = vi.fn(); - -vi.mock("../../../shared/readFileSafeAsJson.js", () => ({ - get readFileSafeAsJson() { - return mockReadFileSafeAsJson; - }, -})); - -const options = { - access: "public", - author: "test-author", - base: "everything", - description: "Test description.", - directory: ".", - email: { - github: "github@email.com", - npm: "npm@email.com", - }, - mode: "create", - owner: "test-owner", - repository: "test-repository", - title: "", -} satisfies Options; - -describe("writePackageJson", () => { - it("preserves existing dependencies when they exist", async () => { - const dependencies = { abc: "1.2.3" }; - mockReadFileSafeAsJson.mockResolvedValue({ dependencies }); - - const packageJson = await writePackageJson(options); - - expect(JSON.parse(packageJson)).toEqual( - expect.objectContaining({ dependencies }), - ); - }); - - it("preserves existing devDependencies that aren't known to be unnecessary when they exist", async () => { - const devDependencies = { abc: "1.2.3", jest: "4.5.6" }; - mockReadFileSafeAsJson.mockResolvedValue({ devDependencies }); - - const packageJson = await writePackageJson(options); - - expect(JSON.parse(packageJson)).toEqual( - expect.objectContaining({ devDependencies }), - ); - }); - - it("includes flattened keywords when they're specified", async () => { - mockReadFileSafeAsJson.mockResolvedValue({}); - - const keywords = ["abc", "def ghi", "jkl mno pqr"]; - const packageJson = await writePackageJson({ ...options, keywords }); - - expect(JSON.parse(packageJson)).toEqual( - expect.objectContaining({ - keywords: ["abc", "def", "ghi", "jkl", "mno", "pqr"], - }), - ); - }); - - it("includes all optional portions when no exclusions are enabled", async () => { - mockReadFileSafeAsJson.mockResolvedValue({}); - - const packageJson = await writePackageJson(options); - - expect(JSON.parse(packageJson)).toMatchInlineSnapshot(` - { - "author": { - "email": "npm@email.com", - "name": "test-author", - }, - "description": "Test description.", - "devDependencies": {}, - "engines": { - "node": ">=18.3.0", - }, - "files": [ - "LICENSE.md", - "README.md", - "lib/", - "package.json", - ], - "license": "MIT", - "lint-staged": { - "*": "prettier --ignore-unknown --write", - }, - "main": "./lib/index.js", - "name": "test-repository", - "publishConfig": { - "provenance": true, - }, - "repository": { - "type": "git", - "url": "git+https://github.com/test-owner/test-repository.git", - }, - "scripts": { - "build": "tsup", - "format": "prettier .", - "lint": "eslint . --max-warnings 0", - "lint:knip": "knip", - "lint:md": "markdownlint "**/*.md" ".github/**/*.md" --rules sentences-per-line", - "lint:packages": "pnpm dedupe --check", - "lint:spelling": "cspell "**" ".github/**/*"", - "prepare": "husky", - "test": "vitest", - "tsc": "tsc", - }, - "type": "module", - "version": "0.0.0", - } - `); - }); - - it("excludes all optional portions when all exclusions are enabled", async () => { - mockReadFileSafeAsJson.mockResolvedValue({}); - - const packageJson = await writePackageJson({ - ...options, - bin: "./bin/index.js", - excludeAllContributors: true, - excludeBuild: true, - excludeCompliance: true, - excludeLintJson: true, - excludeLintKnip: true, - excludeLintMd: true, - excludeLintPackageJson: true, - excludeLintPackages: true, - excludeLintPerfectionist: true, - excludeLintSpelling: true, - excludeLintYml: true, - excludeReleases: true, - excludeRenovate: true, - excludeTests: true, - }); - - expect(JSON.parse(packageJson)).toMatchInlineSnapshot(` - { - "author": { - "email": "npm@email.com", - "name": "test-author", - }, - "bin": "./bin/index.js", - "description": "Test description.", - "devDependencies": {}, - "engines": { - "node": ">=18.3.0", - }, - "files": [ - "LICENSE.md", - "README.md", - "bin/index.js", - "lib/", - "package.json", - ], - "license": "MIT", - "lint-staged": { - "*": "prettier --ignore-unknown --write", - }, - "main": "./lib/index.js", - "name": "test-repository", - "publishConfig": { - "provenance": true, - }, - "repository": { - "type": "git", - "url": "git+https://github.com/test-owner/test-repository.git", - }, - "scripts": { - "format": "prettier .", - "lint": "eslint . --max-warnings 0", - "prepare": "husky", - "tsc": "tsc", - }, - "type": "module", - "version": "0.0.0", - } - `); - }); -}); diff --git a/src/steps/writing/creation/writePackageJson.ts b/src/steps/writing/creation/writePackageJson.ts deleted file mode 100644 index 742acc391..000000000 --- a/src/steps/writing/creation/writePackageJson.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { sortPackageJson } from "sort-package-json"; - -import { readFileSafeAsJson } from "../../../shared/readFileSafeAsJson.js"; -import { Options, PartialPackageData } from "../../../shared/types.js"; -import { formatJson } from "./formatters/formatJson.js"; - -const devDependenciesToRemove = [ - "@babel/core", - "@babel/preset-env", - "@babel/preset-react", - "@babel/preset-typescript", - "@swc/jest", - "@vitest/coverage-istanbul", - "ava", - "babel-jest", - "commitlint", - "cson-parser", - "esbuild", - "eslint-config-prettier", - "eslint-plugin-import", - "eslint-plugin-jest", - "eslint-plugin-prettier", - "eslint-plugin-simple-import-sort", - "eslint-plugin-typescript-sort-keys", - "jasmine", - "jest", - "mocha", - "npm-run-all", - "pnpm-deduplicate", - "pretty-quick", - "ts-jest", -]; - -export async function writePackageJson(options: Options) { - const existingPackageJson = - ((await readFileSafeAsJson( - "./package.json", - )) as null | PartialPackageData) ?? {}; - - return sortPackageJson( - await formatJson({ - // If we didn't already have a version, set it to 0.0.0 - version: "0.0.0", - - // To start, copy over all existing package fields (e.g. "dependencies") - ...existingPackageJson, - - author: { email: options.email.npm, name: options.author }, - bin: options.bin, - description: options.description, - keywords: options.keywords?.length - ? options.keywords.flatMap((keyword) => keyword.split(/ /)) - : undefined, - - // We copy all existing dev dependencies except those we know are not used anymore - devDependencies: { - ...copyDevDependencies(existingPackageJson), - // prettier is a special case of being switched to a devDependency - prettier: existingPackageJson.dependencies?.prettier, - }, - - // Remove fields we know we don't want, such as old or redundant configs - eslintConfig: undefined, - husky: undefined, - jest: undefined, - mocha: undefined, - prettierConfig: undefined, - types: undefined, - - // The rest of the fields are ones we know from our template - engines: { - node: ">=18.3.0", - }, - files: [ - options.bin?.replace(/^\.\//, ""), - "lib/", - "package.json", - "LICENSE.md", - "README.md", - ] - .filter(Boolean) - .sort(), - license: "MIT", - "lint-staged": { - "*": "prettier --ignore-unknown --write", - }, - main: "./lib/index.js", - name: options.repository, - publishConfig: { - provenance: true, - }, - repository: { - type: "git", - url: `git+https://github.com/${options.owner}/${options.repository}.git`, - }, - scripts: { - ...existingPackageJson.scripts, - ...(!options.excludeBuild && { - build: "tsup", - }), - format: "prettier .", - lint: "eslint . --max-warnings 0", - ...(!options.excludeLintKnip && { - "lint:knip": "knip", - }), - ...(!options.excludeLintMd && { - "lint:md": - 'markdownlint "**/*.md" ".github/**/*.md" --rules sentences-per-line', - }), - ...(!options.excludeLintPackages && { - "lint:packages": "pnpm dedupe --check", - }), - ...(!options.excludeLintSpelling && { - "lint:spelling": 'cspell "**" ".github/**/*"', - }), - prepare: "husky", - ...(!options.excludeTests && { test: "vitest" }), - tsc: "tsc", - }, - type: "module", - }), - ); -} - -function copyDevDependencies(existingPackageJson: object) { - const devDependencies = - "devDependencies" in existingPackageJson - ? (existingPackageJson.devDependencies as Record) - : {}; - - for (const devDependencyToRemove of devDependenciesToRemove) { - // eslint-disable-next-line @typescript-eslint/no-dynamic-delete - delete devDependencies[devDependencyToRemove]; - } - - return devDependencies; -} diff --git a/src/steps/writing/types.ts b/src/steps/writing/types.ts deleted file mode 100644 index 20b1f30f1..000000000 --- a/src/steps/writing/types.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface Structure { - [i: string]: false | string | Structure | undefined; -} diff --git a/src/steps/writing/writeStructure.test.ts b/src/steps/writing/writeStructure.test.ts deleted file mode 100644 index 63dcf8520..000000000 --- a/src/steps/writing/writeStructure.test.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; - -import { Options } from "../../shared/types.js"; -import { writeStructure } from "./writeStructure.js"; - -const mock$ = vi.fn(); - -vi.mock("execa", () => ({ - get $() { - return mock$; - }, -})); - -vi.mock("./creation/index.js"); -vi.mock("./writeStructureWorker.js"); - -const options = { - access: "public", - author: "TestAuthor", - base: "everything", - description: "Test description.", - directory: ".", - email: { - github: "github@email.com", - npm: "npm@email.com", - }, - keywords: ["abc", "def ghi", "jkl mno pqr"], - mode: "create", - owner: "TestOwner", - repository: "test-repository", - title: "Test Title", -} satisfies Options; - -describe("writeStructure", () => { - it("resolves when chmod resolves", async () => { - mock$.mockResolvedValue(undefined); - - await expect(writeStructure(options)).resolves.toBeUndefined(); - }); - - it("resolves when chmod rejects", async () => { - mock$.mockRejectedValue(new Error("Oh no!")); - - await expect(writeStructure(options)).resolves.toBeUndefined(); - }); -}); diff --git a/src/steps/writing/writeStructure.ts b/src/steps/writing/writeStructure.ts deleted file mode 100644 index 1a5531f8e..000000000 --- a/src/steps/writing/writeStructure.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { applyFilesToSystem, createWritingFileSystem } from "create"; -import { $ } from "execa"; - -import { isUsingCreateEngine } from "../../shared/isUsingCreateEngine.js"; -import { Options } from "../../shared/types.js"; -import { createStructure } from "./creation/index.js"; -import { writeStructureWorker } from "./writeStructureWorker.js"; - -export async function writeStructure(options: Options) { - const usingCreateEngine = isUsingCreateEngine(); - const structure = await createStructure(options, usingCreateEngine); - - if (usingCreateEngine) { - await applyFilesToSystem(structure, createWritingFileSystem(), "."); - return; - } - - await writeStructureWorker(structure, "."); - - try { - // https://github.com/JoshuaKGoldberg/create-typescript-app/issues/718 - await $`chmod ug+x .husky/pre-commit`; - } catch { - // https://github.com/JoshuaKGoldberg/create-typescript-app/issues/1195 - } -} diff --git a/src/steps/writing/writeStructureWorker.test.ts b/src/steps/writing/writeStructureWorker.test.ts deleted file mode 100644 index 92c03b1c9..000000000 --- a/src/steps/writing/writeStructureWorker.test.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; - -import { writeStructureWorker } from "./writeStructureWorker.js"; - -const mockMkdir = vi.fn(); -const mockWriteFile = vi.fn(); - -vi.mock("node:fs/promises", () => ({ - get mkdir() { - return mockMkdir; - }, - get writeFile() { - return mockWriteFile; - }, -})); - -describe("writeStructureWorker", () => { - it("writes an unformatted file when structure has a file", async () => { - await writeStructureWorker( - { - file: "content", - }, - ".", - ); - - expect(mockMkdir).toHaveBeenCalledWith(".", { recursive: true }); - expect(mockWriteFile).toHaveBeenCalledWith("file", "content"); - }); - - it.each([ - ["implicit json", ".rc", '{ "value": true }', '{ "value": true }\n'], - ["cjs", "file.cjs", " module.exports = { };", "module.exports = {};\n"], - ["js", "file.js", " export default { }", "export default {};\n"], - ["explicit json", "file.json", "{ }", "{}\n"], - ["md", "file.md", " # h1 ", "# h1\n"], - ["yml", "file.yml", " on: true ", "on: true\n"], - ])("writes a formatted %s file", async (_, file, input, output) => { - await writeStructureWorker({ [file]: input }, "."); - expect(mockWriteFile).toHaveBeenCalledWith(file, output); - }); - - it("writes a nested file when structure has a file inside a directory", async () => { - await writeStructureWorker( - { - directory: { - file: "content", - }, - }, - ".", - ); - - expect(mockMkdir).toHaveBeenCalledWith(".", { recursive: true }); - expect(mockMkdir).toHaveBeenCalledWith("directory", { recursive: true }); - }); -}); diff --git a/src/steps/writing/writeStructureWorker.ts b/src/steps/writing/writeStructureWorker.ts deleted file mode 100644 index 03a89dccb..000000000 --- a/src/steps/writing/writeStructureWorker.ts +++ /dev/null @@ -1,55 +0,0 @@ -import * as fs from "node:fs/promises"; -import * as path from "path"; -import prettier from "prettier"; - -import { Structure } from "./types.js"; - -export async function writeStructureWorker( - structure: Structure, - basePath: string, -) { - await fs.mkdir(basePath, { recursive: true }); - - for (const [fileName, contents] of Object.entries(structure)) { - if (typeof contents === "string") { - await fs.writeFile( - path.join(basePath, fileName), - await format(fileName, contents), - ); - } else if (contents) { - await writeStructureWorker(contents, path.join(basePath, fileName)); - } - } -} - -async function format(fileName: string, text: string) { - const parser = inferParser(fileName, text); - if (!parser) { - return text; - } - - return await prettier.format(text, { - parser, - useTabs: true, - }); -} - -function inferParser(fileName: string, text: string) { - if (text.startsWith("{")) { - return "json"; - } - - switch (fileName.split(".").at(-1)) { - case "cjs": - case "js": - return "babel"; - case "json": - return "json"; - case "md": - return "markdown"; - case "yml": - return "yaml"; - } - - return undefined; -} diff --git a/src/next/template.ts b/src/template.ts similarity index 56% rename from src/next/template.ts rename to src/template.ts index c2e8318f9..1f71d265d 100644 --- a/src/next/template.ts +++ b/src/template.ts @@ -1,7 +1,7 @@ import { base } from "./base.js"; -import { presetCommon } from "./presets/presetCommon.js"; -import { presetEverything } from "./presets/presetEverything.js"; -import { presetMinimal } from "./presets/presetMinimal.js"; +import { presetCommon } from "./presets/common.js"; +import { presetEverything } from "./presets/everything.js"; +import { presetMinimal } from "./presets/minimal.js"; export const template = base.createTemplate({ about: { diff --git a/src/types.ts b/src/types.ts index 4f16ae396..e4f372994 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,5 +1,25 @@ -export interface GreetOptions { - logger?: (message: string) => void; - message: string; - times?: number; +export interface AllContributorContributor { + avatar_url: string; + contributions: string[]; + login: string; + name: string; + profile: string; +} + +export interface AllContributorsData { + contributors: AllContributorContributor[]; +} + +export interface PartialPackageData { + author?: string | { email: string; name: string }; + bin?: string; + dependencies?: Record; + description?: string; + devDependencies?: Record; + email?: string; + engines?: { node?: string }; + name?: string; + repository?: string | { type: string; url: string }; + scripts?: Record; + version?: string; } diff --git a/src/next/utils/resolveBin.ts b/src/utils/resolveBin.ts similarity index 100% rename from src/next/utils/resolveBin.ts rename to src/utils/resolveBin.ts diff --git a/src/next/utils/sortObject.ts b/src/utils/sortObject.ts similarity index 100% rename from src/next/utils/sortObject.ts rename to src/utils/sortObject.ts diff --git a/src/next/utils/swallowError.ts b/src/utils/swallowError.ts similarity index 100% rename from src/next/utils/swallowError.ts rename to src/utils/swallowError.ts diff --git a/src/shared/tryCatchAsync.ts b/src/utils/tryCatchAsync.ts similarity index 100% rename from src/shared/tryCatchAsync.ts rename to src/utils/tryCatchAsync.ts diff --git a/src/shared/tryCatchLazyValueAsync.test.ts b/src/utils/tryCatchLazyValueAsync.test.ts similarity index 100% rename from src/shared/tryCatchLazyValueAsync.test.ts rename to src/utils/tryCatchLazyValueAsync.test.ts diff --git a/src/shared/tryCatchLazyValueAsync.ts b/src/utils/tryCatchLazyValueAsync.ts similarity index 100% rename from src/shared/tryCatchLazyValueAsync.ts rename to src/utils/tryCatchLazyValueAsync.ts diff --git a/tsconfig.json b/tsconfig.json index 1175f31f1..223cce102 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,5 +12,5 @@ "strict": true, "target": "ES2022" }, - "include": ["src", "script"] + "include": ["src"] } From 6ec87e6142fc2d7a136d16e7292105d8e2785f9f Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Tue, 14 Jan 2025 13:34:21 -0500 Subject: [PATCH 02/22] Use create-fs intakeFromDirectory and create-testers diffCreatedDirectory --- .DS_Store | Bin 6148 -> 0 bytes .github/DEVELOPMENT.md | 2 +- .github/codecov.yml | 5 - .github/renovate.json | 1 - .github/workflows/ci.yml | 47 -- .gitignore | 2 +- .prettierignore | 2 +- README.md | 105 +-- create.config.ts | 56 ++ cspell.json | 10 +- eslint.config.js | 25 +- knip.json | 4 +- package.json | 7 +- pnpm-lock.yaml | 709 +++++++++++--------- src/base.ts | 24 +- src/blocks/blockAreTheTypesWrong.ts | 1 + src/blocks/blockCodecov.ts | 21 + src/blocks/blockDevelopmentDocs.test.ts | 8 +- src/blocks/blockDevelopmentDocs.ts | 2 +- src/blocks/blockESLint.ts | 21 +- src/blocks/blockESLintNode.ts | 14 +- src/blocks/blockKnip.ts | 13 +- src/blocks/blockMarkdownlint.ts | 17 +- src/blocks/blockPackageJson.ts | 4 +- src/blocks/blockPrettier.ts | 9 +- src/blocks/blockREADME.ts | 86 ++- src/blocks/blockTSup.ts | 8 +- src/blocks/blockVitest.test.ts | 9 +- src/blocks/blockVitest.ts | 15 +- src/blocks/files/createMultiWorkflowFile.ts | 2 +- src/blocks/files/createSoloWorkflowFile.ts | 2 +- src/docs.test.ts | 18 +- src/index.test.ts | 16 - src/integration.test.ts | 128 ++++ src/options/readDefaultsFromReadme.ts | 6 + src/options/readLogoSizing.ts | 2 +- src/presets/common.ts | 2 + 37 files changed, 858 insertions(+), 545 deletions(-) delete mode 100644 .DS_Store delete mode 100644 .github/codecov.yml create mode 100644 create.config.ts create mode 100644 src/blocks/blockCodecov.ts delete mode 100644 src/index.test.ts create mode 100644 src/integration.test.ts diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 69944ba1a5be222e2807859e027cd7d2fb74ba4c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK%}T>S5Z<*_6N-?7LXQhx3)ZTI;w8lT0!H+pQWH}&7_%iw&7l->))(?gd>&_Z zH()Vm5jz9B-~8@oKgj+t#<(|+4jFS8V-_?-j!J`|yD_w3k`XzMkrDf#o){no8W_O+K|n)v4OSY})&U(}pE2G>L;)S&5{SZ}Yp~J?5fH9R0d*-iPYkZh z!7ognYp~L&%NbWQ!#HN<`ticm?BEwFopDzq^~3-%u*yJ7n+~4;=kUukKJr&f$RY-a zfq%vTZ;$=42a7Ui>yPE(Su3F3Lqoy53KbC0S1tiy;6Bn-K^+&UL!N7}(ukv=UzG#W OML-ck9Wn3=4158H$4Q9* diff --git a/.github/DEVELOPMENT.md b/.github/DEVELOPMENT.md index c7187f848..fe35abe40 100644 --- a/.github/DEVELOPMENT.md +++ b/.github/DEVELOPMENT.md @@ -6,7 +6,7 @@ After [forking the repo from GitHub](https://help.github.com/articles/fork-a-repo) and [installing pnpm](https://pnpm.io/installation): ```shell -git clone https://github.com/ < your-name-here > /create-typescript-app +git clone https://github.com/(your-name-here)/create-typescript-app cd create-typescript-app pnpm install ``` diff --git a/.github/codecov.yml b/.github/codecov.yml deleted file mode 100644 index 70976385a..000000000 --- a/.github/codecov.yml +++ /dev/null @@ -1,5 +0,0 @@ -codecov: - notify: - after_n_builds: 4 -comment: - after_n_builds: 4 diff --git a/.github/renovate.json b/.github/renovate.json index a11ec162e..6d50b3cfc 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -2,7 +2,6 @@ "$schema": "https://docs.renovatebot.com/renovate-schema.json", "automerge": true, "extends": ["config:best-practices", "replacements:all"], - "github-actions": { "enabled": false }, "ignoreDeps": ["codecov/codecov-action"], "labels": ["dependencies"], "minimumReleaseAge": "7 days", diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 39a417b1e..8237cc06e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,53 +69,6 @@ jobs: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} if: always() uses: codecov/codecov-action@v3 - with: - flags: unit - test_creation_script: - name: Test Creation Script - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/prepare - - run: pnpm run build - - run: pnpm run test:create - - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - if: always() - uses: codecov/codecov-action@v3 - with: - files: coverage-create/lcov.info - flags: create - test_initialization_script: - name: Test Initialization Script - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/prepare - - run: pnpm run build - - run: pnpm run test:initialize - - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - if: always() - uses: codecov/codecov-action@v3 - with: - files: coverage-initialize/lcov.info - flags: initialize - test_migration_script: - name: Test Migration Script - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/prepare - - run: pnpm run build - - run: pnpm run test:migrate - - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - if: always() - uses: codecov/codecov-action@v3 - with: - files: coverage-migrate/lcov.info - flags: migrate type_check: name: Type Check runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index cd60c28d4..67a4fbfa1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ -/coverage* +/coverage /lib /node_modules diff --git a/.prettierignore b/.prettierignore index 24fb38510..2663f9c39 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,4 +1,4 @@ /.husky -/coverage* +/coverage /lib /pnpm-lock.yaml diff --git a/README.md b/README.md index a5894ebbe..97fc7ceea 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@

Create TypeScript App

-

Quickstart-friendly TypeScript template with comprehensive, configurable, opinionated tooling. 🎁

+

+ Quickstart-friendly TypeScript template with comprehensive, configurable, opinionated tooling. + 🎁 +

@@ -20,7 +23,7 @@ `create-typescript-app` is a one-stop-shop solution to set up a new or existing repository with the latest and greatest TypeScript tooling. It includes options not just for building and testing but also GitHub repository templates, contributor recognition, automated release management, and more. -## Getting Started +## Usage First make sure you have the following installed: @@ -69,72 +72,72 @@ Thanks! 💖 - - - - - - + + + + + + - - - - - - - + + + + + + + - + - - - - - + + + + + - - - - - - - + + + + + + + - - - + + + - - - + + + - - - - - - - + + + + + + + - - - - - - - + + + + + + + - - - + + +
Alex / KATT
Alex / KATT

🐛
Anurag
Anurag

💻
Atila Fassina
Atila Fassina

🐛
Biplob Sutradhar
Biplob Sutradhar

💻
Conor Meagher
Conor Meagher

💻
Dan Vanderkam
Dan Vanderkam

🐛 🤔 🔧
Alex / KATT
Alex / KATT

🐛
Anurag
Anurag

💻
Atila Fassina
Atila Fassina

🐛
Biplob Sutradhar
Biplob Sutradhar

💻
Conor Meagher
Conor Meagher

💻
Dan Vanderkam
Dan Vanderkam

🐛 🤔 🔧
Daniel
Daniel

🚇
Daniel Roe
Daniel Roe

💻
Demian Parkhomenko
Demian Parkhomenko

🐛 💻
Dominic Duffin
Dominic Duffin

💻
Dominik Nowik
Dominik Nowik

🔧 💻 🤔
Emerson
Emerson

💻
Faraz Patankar
Faraz Patankar

🐛
Felix Boehm
Felix Boehm

🐛
Daniel Roe
Daniel Roe

💻
Demian Parkhomenko
Demian Parkhomenko

🐛 💻
Dominic Duffin
Dominic Duffin

💻
Dominik Nowik
Dominik Nowik

🔧 💻 🤔
Emerson
Emerson

💻
Faraz Patankar
Faraz Patankar

🐛
Felix Boehm
Felix Boehm

🐛
Graham Vasquez
Graham Vasquez

💻
Graham Vasquez
Graham Vasquez

💻
Jamie Magee
Jamie Magee

🤔
Jan-Niklas W.
Jan-Niklas W.

💻
Jeff Wen
Jeff Wen

💻
Jessica Wilkins
Jessica Wilkins

💻
Joe Previte
Joe Previte

🐛 💻
John Reilly
John Reilly

💻 🤔 🐛 🚧 📖 🔧
Jan-Niklas W.
Jan-Niklas W.

💻
Jeff Wen
Jeff Wen

💻
Jessica Wilkins
Jessica Wilkins

💻
Joe Previte
Joe Previte

🐛 💻
John Reilly
John Reilly

💻 🤔 🐛 🚧 📖 🔧
Josh Goldberg
Josh Goldberg

🐛 💻 🚧 👀 🔧 📖 🚇 ⚠️ 🤔
Joël Galeran
Joël Galeran

💻
Juan A.
Juan A.

💻 📖
Kristo Baricevic
Kristo Baricevic

💻
Lars Kappert
Lars Kappert

💻
Mohammad Bagher Abiyat
Mohammad Bagher Abiyat

💻
Navin Moorthy
Navin Moorthy

🐛 💻 🤔
Josh Goldberg
Josh Goldberg

🐛 💻 🚧 👀 🔧 📖 🚇 ⚠️ 🤔 🎨
Joël Galeran
Joël Galeran

💻
Juan A.
Juan A.

💻 📖
Kristo Baricevic
Kristo Baricevic

💻
Lars Kappert
Lars Kappert

💻
Mohammad Bagher Abiyat
Mohammad Bagher Abiyat

💻
Navin Moorthy
Navin Moorthy

🐛 💻 🤔
NazCodeland
NazCodeland

💻
Orta Therox
Orta Therox

💻 🤔
Paul Esch-Laurent
Paul Esch-Laurent

💻
NazCodeland
NazCodeland

💻
Orta Therox
Orta Therox

💻 🤔
Paul Esch-Laurent
Paul Esch-Laurent

💻
Pelle Wessman
Pelle Wessman

🤔
Praveen Shinde
Praveen Shinde

💻
Promise Dash
Promise Dash

💻
Rebecca Stevens
Rebecca Stevens

💻 🚇
Praveen Shinde
Praveen Shinde

💻
Promise Dash
Promise Dash

💻
Rebecca Stevens
Rebecca Stevens

💻 🚇
Ron Braha
Ron Braha

💻 🎨 ⚠️
Ron Jean-Francois
Ron Jean-Francois

💻 🚇
Ruthwik
Ruthwik

💻
Ryota Murakami
Ryota Murakami

💻 🐛
Shraddha
Shraddha

💻
Sudhansu
Sudhansu

💻
Swastik Patel
Swastik Patel

📖
Ron Braha
Ron Braha

💻 🎨 ⚠️
Ron Jean-Francois
Ron Jean-Francois

💻 🚇
Ruthwik
Ruthwik

💻
Ryota Murakami
Ryota Murakami

💻 🐛
Shraddha
Shraddha

💻
Sudhansu
Sudhansu

💻
Swastik Patel
Swastik Patel

📖
Timon Jurschitsch
Timon Jurschitsch

💻
Tung Bui (Leo)
Tung Bui (Leo)

💻
Valon
Valon

📖
Vasanth Kumar Cheepurupalli
Vasanth Kumar Cheepurupalli

💻
Xiaomin Liu
Xiaomin Liu

💻
lcforbes
lcforbes

🐛
michael faith
michael faith

💻
Timon Jurschitsch
Timon Jurschitsch

💻
Tung Bui (Leo)
Tung Bui (Leo)

💻
Valon
Valon

📖
Vasanth Kumar Cheepurupalli
Vasanth Kumar Cheepurupalli

💻
Xiaomin Liu
Xiaomin Liu

💻
lcforbes
lcforbes

🐛
michael faith
michael faith

💻
nandertga
nandertga

💻
rubiesonthesky
rubiesonthesky

🤔 💻
takanomedev
takanomedev

💻
nandertga
nandertga

💻
rubiesonthesky
rubiesonthesky

🤔 💻
takanomedev
takanomedev

💻
diff --git a/create.config.ts b/create.config.ts new file mode 100644 index 000000000..01bb5e71c --- /dev/null +++ b/create.config.ts @@ -0,0 +1,56 @@ +import { createConfig } from "create"; + +import { + blockAreTheTypesWrong, + blockESLint, + blockTemplatedWith, + blockTSup, + presets, +} from "./src/index.ts"; + +export default createConfig(presets.everything, { + addons: [ + blockESLint({ + explanations: [ + `👋 Hi! This ESLint configuration contains a lot more stuff than many repos'! +You can read from it to see all sorts of linting goodness, but don't worry - +it's not something you need to exhaustively understand immediately. 💙 + +If you're interested in learning more, see the 'getting started' docs on: +- ESLint: https://eslint.org +- typescript-eslint: https://typescript-eslint.io`, + ], + rules: [ + { + comment: + "These on-by-default rules work well for this repo if configured", + entries: { + "@typescript-eslint/no-unnecessary-condition": [ + "error", + { allowConstantLoopConditions: true }, + ], + "@typescript-eslint/prefer-nullish-coalescing": [ + "error", + { ignorePrimitives: true }, + ], + "@typescript-eslint/restrict-template-expressions": [ + "error", + { allowBoolean: true, allowNullish: true, allowNumber: true }, + ], + "n/no-unsupported-features/node-builtins": [ + "error", + { allowExperimental: true }, + ], + }, + }, + ], + }), + blockTSup({ + runArgs: ["--version"], + }), + ], + blocks: { + add: [blockAreTheTypesWrong], + exclude: [blockTemplatedWith], + }, +}); diff --git a/cspell.json b/cspell.json index 0a9089663..924e84d96 100644 --- a/cspell.json +++ b/cspell.json @@ -4,21 +4,21 @@ ".all-contributorsrc", ".github", "CHANGELOG.md", - "coverage*", + "coverage", "lib", "node_modules", - "pnpm-lock.yaml", - "script/__snapshots__" + "pnpm-lock.yaml" ], "words": [ "Anson", "apexskier", + "automerge", "dbaeumer", "infile", "joshuakgoldberg", "markdownlintignore", "mtfoley", - "ruleset", - "rulesets" + "npmjs", + "tseslint" ] } diff --git a/eslint.config.js b/eslint.config.js index 648b4ee1b..957335924 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -23,13 +23,7 @@ import tseslint from "typescript-eslint"; export default tseslint.config( { - ignores: [ - "**/*.snap", - "coverage*", - "lib", - "node_modules", - "pnpm-lock.yaml", - ], + ignores: ["**/*.snap", "coverage", "lib", "node_modules", "pnpm-lock.yaml"], }, { linterOptions: { reportUnusedDisableDirectives: "error" } }, eslint.configs.recommended, @@ -52,19 +46,13 @@ export default tseslint.config( languageOptions: { parserOptions: { projectService: { - allowDefaultProject: ["*.config.*s", "bin/*.js"], + allowDefaultProject: ["*.config.*s", "./bin/index.js"], }, tsconfigRootDir: import.meta.dirname, }, }, rules: { // These on-by-default rules work well for this repo if configured - "@typescript-eslint/no-unnecessary-condition": [ - "error", - { - allowConstantLoopConditions: true, - }, - ], "@typescript-eslint/prefer-nullish-coalescing": [ "error", { ignorePrimitives: true }, @@ -73,10 +61,6 @@ export default tseslint.config( "error", { allowBoolean: true, allowNullish: true, allowNumber: true }, ], - "n/no-unsupported-features/node-builtins": [ - "error", - { allowExperimental: true }, - ], // Stylistic concerns that don't interfere with Prettier "logical-assignment-operators": [ @@ -90,10 +74,7 @@ export default tseslint.config( }, settings: { perfectionist: { partitionByComment: true, type: "natural" } }, }, - { - extends: [tseslint.configs.disableTypeChecked], - files: ["**/*.md/*.ts"], - }, + { extends: [tseslint.configs.disableTypeChecked], files: ["**/*.md/*.ts"] }, { extends: [vitest.configs.recommended], files: ["**/*.test.*"], diff --git a/knip.json b/knip.json index 011f1bccb..16c4fc7e1 100644 --- a/knip.json +++ b/knip.json @@ -1,7 +1,7 @@ { "$schema": "https://unpkg.com/knip@5.41.1/schema.json", - "entry": ["script/*e2e.js", "src/index.ts!", "src/**/*.test.*"], + "entry": ["src/index.ts", "src/**/*.test.*"], "ignoreDependencies": ["all-contributors-cli", "cspell-populate-words"], "ignoreExportsUsedInFile": { "interface": true, "type": true }, - "project": ["src/**/*.ts!", "script/**/*.js"] + "project": ["src/**/*.ts"] } diff --git a/package.json b/package.json index caaa9fd58..ca74bf951 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,6 @@ "object-strings-deep": "^0.1.1", "parse-author": "^2.0.0", "parse-package-name": "^1.0.0", - "prettier": "^3.4.2", "remove-undefined-objects": "^5.0.0", "set-github-repository-labels": "^0.1.0", "sort-package-json": "^2.12.0", @@ -64,11 +63,13 @@ "devDependencies": { "@eslint-community/eslint-plugin-eslint-comments": "4.4.1", "@eslint/js": "9.17.0", + "@prettier/sync": "^0.5.2", "@release-it/conventional-changelog": "9.0.3", "@types/eslint-plugin-markdown": "2.0.2", "@types/git-url-parse": "9.0.3", "@types/html-to-text": "9.0.4", "@types/js-yaml": "4.0.9", + "@types/lodash": "^4.17.14", "@types/node": "22.10.2", "@types/parse-author": "2.0.3", "@vitest/coverage-v8": "2.1.8", @@ -76,7 +77,7 @@ "all-contributors-cli": "6.26.1", "console-fail-test": "0.5.0", "create-testers": "0.1.0-alpha.11", - "cspell": "8.17.1", + "cspell": "^8.17.2", "eslint": "9.17.0", "eslint-plugin-jsdoc": "50.6.1", "eslint-plugin-jsonc": "2.18.2", @@ -89,8 +90,10 @@ "husky": "9.1.7", "knip": "5.41.1", "lint-staged": "15.3.0", + "lodash": "^4.17.21", "markdownlint": "0.37.2", "markdownlint-cli": "0.43.0", + "prettier": "^3.4.2", "prettier-plugin-curly": "0.3.1", "prettier-plugin-packagejson": "2.5.6", "prettier-plugin-sh": "0.14.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 325da0b11..2bfc4dc7b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -59,9 +59,6 @@ importers: parse-package-name: specifier: ^1.0.0 version: 1.0.0 - prettier: - specifier: ^3.4.2 - version: 3.4.2 remove-undefined-objects: specifier: ^5.0.0 version: 5.0.0 @@ -84,6 +81,9 @@ importers: '@eslint/js': specifier: 9.17.0 version: 9.17.0 + '@prettier/sync': + specifier: ^0.5.2 + version: 0.5.2(prettier@3.4.2) '@release-it/conventional-changelog': specifier: 9.0.3 version: 9.0.3(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.0.0)(release-it@17.10.0(typescript@5.7.2)) @@ -99,6 +99,9 @@ importers: '@types/js-yaml': specifier: 4.0.9 version: 4.0.9 + '@types/lodash': + specifier: ^4.17.14 + version: 4.17.14 '@types/node': specifier: 22.10.2 version: 22.10.2 @@ -121,8 +124,8 @@ importers: specifier: 0.1.0-alpha.11 version: 0.1.0-alpha.11(create@0.1.0-alpha.11(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.0.0)(typescript@5.7.2)) cspell: - specifier: 8.17.1 - version: 8.17.1 + specifier: ^8.17.2 + version: 8.17.2 eslint: specifier: 9.17.0 version: 9.17.0(jiti@2.4.0) @@ -159,12 +162,18 @@ importers: lint-staged: specifier: 15.3.0 version: 15.3.0 + lodash: + specifier: ^4.17.21 + version: 4.17.21 markdownlint: specifier: 0.37.2 version: 0.37.2 markdownlint-cli: specifier: 0.43.0 version: 0.43.0 + prettier: + specifier: ^3.4.2 + version: 3.4.2 prettier-plugin-curly: specifier: 0.3.1 version: 0.3.1(prettier@3.4.2) @@ -182,7 +191,7 @@ importers: version: 0.3.0 tsup: specifier: 8.3.5 - version: 8.3.5(jiti@2.4.0)(postcss@8.4.36)(tsx@4.19.2)(typescript@5.7.2)(yaml@2.6.1) + version: 8.3.5(jiti@2.4.0)(postcss@8.4.36)(tsx@4.19.2)(typescript@5.7.2)(yaml@2.7.0) typescript: specifier: 5.7.2 version: 5.7.2 @@ -268,217 +277,223 @@ packages: conventional-commits-parser: optional: true - '@cspell/cspell-bundled-dicts@8.17.1': - resolution: {integrity: sha512-HmkXS5uX4bk/XxsRS4Q+zRvhgRa81ddGiR2/Xfag9MIi5L5UnEJ4g21EpmIlXkMxYrTu2fp69SZFss5NfcFF9Q==} + '@cspell/cspell-bundled-dicts@8.17.2': + resolution: {integrity: sha512-t+DQtruJF2cYfXF5GC4F0O/PQR04hL5WH55R9oOaor5i7K8ejbw6+jex2LB0XbZFf3qBhXNSnMPuM3b/113LnA==} engines: {node: '>=18'} - '@cspell/cspell-json-reporter@8.17.1': - resolution: {integrity: sha512-EV9Xkh42Xw3aORvDZfxusICX91DDbqQpYdGKBdPGuhgxWOUYYZKpLXsHCmDkhruMPo2m5gDh++/OqjLRPZofKQ==} + '@cspell/cspell-json-reporter@8.17.2': + resolution: {integrity: sha512-9QFzuSApaK7SYB50iCTRIUDjFZf6DXTFj8+qTR2cky+/YmEcHDpJieJVCd3STONO4m2JyqIsV7faEuA6M0YcHg==} engines: {node: '>=18'} - '@cspell/cspell-pipe@8.17.1': - resolution: {integrity: sha512-uhC99Ox+OH3COSgShv4fpVHiotR70dNvAOSkzRvKVRzV6IGyFnxHjmyVVPEV0dsqzVLxltwYTqFhwI+UOwm45A==} + '@cspell/cspell-pipe@8.17.2': + resolution: {integrity: sha512-LOTKK+hZSUc7vaN8SBEOcv+9dMYbo84awbsjjdI+HkKVBfTt3Lzlu6IJImw39L6pTDAJ1ZxOUdWO89jcxpyihg==} engines: {node: '>=18'} - '@cspell/cspell-resolver@8.17.1': - resolution: {integrity: sha512-XEK2ymTdQNgsV3ny60VkKzWskbICl4zNXh/DbxsoRXHqIRg43MXFpTNkEJ7j873EqdX7BU4opQQ+5D4stWWuhQ==} + '@cspell/cspell-resolver@8.17.2': + resolution: {integrity: sha512-Z2ndlzVIiXOCBnQby9q+OXcxeddiuCi//pnhO9Jf6Ixgthn+Yg7bwzAnHu+CM1SJaQnZCntGyimdxfojm+WDdA==} engines: {node: '>=18'} - '@cspell/cspell-service-bus@8.17.1': - resolution: {integrity: sha512-2sFWQtMEWZ4tdz7bw0bAx4NaV1t0ynGfjpuKWdQppsJFKNb+ZPZZ6Ah1dC13AdRRMZaG194kDRFwzNvRaCgWkQ==} + '@cspell/cspell-service-bus@8.17.2': + resolution: {integrity: sha512-Cp4kVxJRyyDRd5RVTASlu+ygWG+dgy6GyH7lzb6P8SOXt1mxzCBK6Q5Dc1XHAsvhRaLrnMziCO/5Pj9/0DKs6w==} engines: {node: '>=18'} - '@cspell/cspell-types@8.17.1': - resolution: {integrity: sha512-NJbov7Jp57fh8addoxesjb8atg/APQfssCH5Q9uZuHBN06wEJDgs7fhfE48bU+RBViC9gltblsYZzZZQKzHYKg==} + '@cspell/cspell-types@8.17.2': + resolution: {integrity: sha512-4kMBhX92p0pchEzYTpyLCoe/bUJ29YYvMINTeHTd//hLQh0ZAyMGY1opDm1tqaXX0qpYmWG60KcvN4fCR0i6lw==} engines: {node: '>=18'} - '@cspell/dict-ada@4.0.5': - resolution: {integrity: sha512-6/RtZ/a+lhFVmrx/B7bfP7rzC4yjEYe8o74EybXcvu4Oue6J4Ey2WSYj96iuodloj1LWrkNCQyX5h4Pmcj0Iag==} + '@cspell/dict-ada@4.1.0': + resolution: {integrity: sha512-7SvmhmX170gyPd+uHXrfmqJBY5qLcCX8kTGURPVeGxmt8XNXT75uu9rnZO+jwrfuU2EimNoArdVy5GZRGljGNg==} - '@cspell/dict-al@1.0.3': - resolution: {integrity: sha512-V1HClwlfU/qwSq2Kt+MkqRAsonNu3mxjSCDyGRecdLGIHmh7yeEeaxqRiO/VZ4KP+eVSiSIlbwrb5YNFfxYZbw==} + '@cspell/dict-al@1.1.0': + resolution: {integrity: sha512-PtNI1KLmYkELYltbzuoztBxfi11jcE9HXBHCpID2lou/J4VMYKJPNqe4ZjVzSI9NYbMnMnyG3gkbhIdx66VSXg==} - '@cspell/dict-aws@4.0.7': - resolution: {integrity: sha512-PoaPpa2NXtSkhGIMIKhsJUXB6UbtTt6Ao3x9JdU9kn7fRZkwD4RjHDGqulucIOz7KeEX/dNRafap6oK9xHe4RA==} + '@cspell/dict-aws@4.0.9': + resolution: {integrity: sha512-bDYdnnJGwSkIZ4gzrauu7qzOs/ZAY/FnU4k11LgdMI8BhwMfsbsy2EI1iS+sD/BI5ZnNT9kU5YR3WADeNOmhRg==} - '@cspell/dict-bash@4.1.8': - resolution: {integrity: sha512-I2CM2pTNthQwW069lKcrVxchJGMVQBzru2ygsHCwgidXRnJL/NTjAPOFTxN58Jc1bf7THWghfEDyKX/oyfc0yg==} + '@cspell/dict-bash@4.2.0': + resolution: {integrity: sha512-HOyOS+4AbCArZHs/wMxX/apRkjxg6NDWdt0jF9i9XkvJQUltMwEhyA2TWYjQ0kssBsnof+9amax2lhiZnh3kCg==} - '@cspell/dict-companies@3.1.9': - resolution: {integrity: sha512-w7XEJ2B6x2jq9ws5XNyYgpYj2MxdZ3jW3PETLxjK7nc8pulCFmaGVgZ0JTnDWfJ3QMOczoagn5f9LM2PZ/CuJg==} + '@cspell/dict-companies@3.1.12': + resolution: {integrity: sha512-99FxBNdLOQc3nVQ663Xh7JqDLbIy/AdqOecQ5bk3HpmXpSkoDvTT7XCUU5nQZvmFBrrQlXFKlRRYjLfTEOUDdA==} - '@cspell/dict-cpp@6.0.2': - resolution: {integrity: sha512-yw5eejWvY4bAnc6LUA44m4WsFwlmgPt2uMSnO7QViGMBDuoeopMma4z9XYvs4lSjTi8fIJs/A1YDfM9AVzb8eg==} + '@cspell/dict-cpp@6.0.3': + resolution: {integrity: sha512-OFrVXdxCeGKnon36Pe3yFjBuY4kzzEwWFf3vDz+cJTodZDkjFkBifQeTtt5YfimgF8cfAJZXkBCsxjipAgmAiw==} - '@cspell/dict-cryptocurrencies@5.0.3': - resolution: {integrity: sha512-bl5q+Mk+T3xOZ12+FG37dB30GDxStza49Rmoax95n37MTLksk9wBo1ICOlPJ6PnDUSyeuv4SIVKgRKMKkJJglA==} + '@cspell/dict-cryptocurrencies@5.0.4': + resolution: {integrity: sha512-6iFu7Abu+4Mgqq08YhTKHfH59mpMpGTwdzDB2Y8bbgiwnGFCeoiSkVkgLn1Kel2++hYcZ8vsAW/MJS9oXxuMag==} - '@cspell/dict-csharp@4.0.5': - resolution: {integrity: sha512-c/sFnNgtRwRJxtC3JHKkyOm+U3/sUrltFeNwml9VsxKBHVmvlg4tk4ar58PdpW9/zTlGUkWi2i85//DN1EsUCA==} + '@cspell/dict-csharp@4.0.6': + resolution: {integrity: sha512-w/+YsqOknjQXmIlWDRmkW+BHBPJZ/XDrfJhZRQnp0wzpPOGml7W0q1iae65P2AFRtTdPKYmvSz7AL5ZRkCnSIw==} - '@cspell/dict-css@4.0.16': - resolution: {integrity: sha512-70qu7L9z/JR6QLyJPk38fNTKitlIHnfunx0wjpWQUQ8/jGADIhMCrz6hInBjqPNdtGpYm8d1dNFyF8taEkOgrQ==} + '@cspell/dict-css@4.0.17': + resolution: {integrity: sha512-2EisRLHk6X/PdicybwlajLGKF5aJf4xnX2uuG5lexuYKt05xV/J/OiBADmi8q9obhxf1nesrMQbqAt+6CsHo/w==} - '@cspell/dict-dart@2.2.4': - resolution: {integrity: sha512-of/cVuUIZZK/+iqefGln8G3bVpfyN6ZtH+LyLkHMoR5tEj+2vtilGNk9ngwyR8L4lEqbKuzSkOxgfVjsXf5PsQ==} + '@cspell/dict-dart@2.3.0': + resolution: {integrity: sha512-1aY90lAicek8vYczGPDKr70pQSTQHwMFLbmWKTAI6iavmb1fisJBS1oTmMOKE4ximDf86MvVN6Ucwx3u/8HqLg==} - '@cspell/dict-data-science@2.0.5': - resolution: {integrity: sha512-nNSILXmhSJox9/QoXICPQgm8q5PbiSQP4afpbkBqPi/u/b3K9MbNH5HvOOa6230gxcGdbZ9Argl2hY/U8siBlg==} + '@cspell/dict-data-science@2.0.6': + resolution: {integrity: sha512-gOYKZOg358yhnnQfr1/f232REmjeIymXUHJdrLEMPirluv2rzMWvEBBazqRVQ++jMUNg9IduVI0v096ZWMDekA==} - '@cspell/dict-django@4.1.3': - resolution: {integrity: sha512-yBspeL3roJlO0a1vKKNaWABURuHdHZ9b1L8d3AukX0AsBy9snSggc8xCavPmSzNfeMDXbH+1lgQiYBd3IW03fg==} + '@cspell/dict-django@4.1.4': + resolution: {integrity: sha512-fX38eUoPvytZ/2GA+g4bbdUtCMGNFSLbdJJPKX2vbewIQGfgSFJKY56vvcHJKAvw7FopjvgyS/98Ta9WN1gckg==} - '@cspell/dict-docker@1.1.11': - resolution: {integrity: sha512-s0Yhb16/R+UT1y727ekbR/itWQF3Qz275DR1ahOa66wYtPjHUXmhM3B/LT3aPaX+hD6AWmK23v57SuyfYHUjsw==} + '@cspell/dict-docker@1.1.12': + resolution: {integrity: sha512-6d25ZPBnYZaT9D9An/x6g/4mk542R8bR3ipnby3QFCxnfdd6xaWiTcwDPsCgwN2aQZIQ1jX/fil9KmBEqIK/qA==} - '@cspell/dict-dotnet@5.0.8': - resolution: {integrity: sha512-MD8CmMgMEdJAIPl2Py3iqrx3B708MbCIXAuOeZ0Mzzb8YmLmiisY7QEYSZPg08D7xuwARycP0Ki+bb0GAkFSqg==} + '@cspell/dict-dotnet@5.0.9': + resolution: {integrity: sha512-JGD6RJW5sHtO5lfiJl11a5DpPN6eKSz5M1YBa1I76j4dDOIqgZB6rQexlDlK1DH9B06X4GdDQwdBfnpAB0r2uQ==} - '@cspell/dict-elixir@4.0.6': - resolution: {integrity: sha512-TfqSTxMHZ2jhiqnXlVKM0bUADtCvwKQv2XZL/DI0rx3doG8mEMS8SGPOmiyyGkHpR/pGOq18AFH3BEm4lViHIw==} + '@cspell/dict-elixir@4.0.7': + resolution: {integrity: sha512-MAUqlMw73mgtSdxvbAvyRlvc3bYnrDqXQrx5K9SwW8F7fRYf9V4vWYFULh+UWwwkqkhX9w03ZqFYRTdkFku6uA==} - '@cspell/dict-en-common-misspellings@2.0.7': - resolution: {integrity: sha512-qNFo3G4wyabcwnM+hDrMYKN9vNVg/k9QkhqSlSst6pULjdvPyPs1mqz1689xO/v9t8e6sR4IKc3CgUXDMTYOpA==} + '@cspell/dict-en-common-misspellings@2.0.8': + resolution: {integrity: sha512-l1u/pDjwrPyWwBd1hCkZhdsK8yLbLFPD2xWz+1tFFI7WaV9ckDZoF3woRc/0wFGRy53yrfSAVuwhoYOQnHe/fA==} '@cspell/dict-en-gb@1.1.33': resolution: {integrity: sha512-tKSSUf9BJEV+GJQAYGw5e+ouhEe2ZXE620S7BLKe3ZmpnjlNG9JqlnaBhkIMxKnNFkLY2BP/EARzw31AZnOv4g==} - '@cspell/dict-en_us@4.3.28': - resolution: {integrity: sha512-BN1PME7cOl7DXRQJ92pEd1f0Xk5sqjcDfThDGkKcsgwbSOY7KnTc/czBW6Pr3WXIchIm6cT12KEfjNqx7U7Rrw==} + '@cspell/dict-en_us@4.3.29': + resolution: {integrity: sha512-7kHP0sJ271oS5RqakxvhWvHFoCUFCBDV6+cgIRIpKwW0aYVB4F2AwElGsdeE/XEmihhYUje7e/e6X3IEWHrcrQ==} - '@cspell/dict-filetypes@3.0.9': - resolution: {integrity: sha512-U7ycC1cE32A5aEgwzp/iE0TVabonUFnVt+Ygbf6NsIWqEuFWZgZChC7gfztA4T1fpuj602nFdp7eOnTWKORsnQ==} + '@cspell/dict-filetypes@3.0.10': + resolution: {integrity: sha512-JEN3627joBVtpa1yfkdN9vz1Z129PoKGHBKjXCEziJvf2Zt1LeULWYYYg/O6pzRR4yzRa5YbXDTuyrN7vX7DFg==} - '@cspell/dict-flutter@1.0.3': - resolution: {integrity: sha512-52C9aUEU22ptpgYh6gQyIdA4MP6NPwzbEqndfgPh3Sra191/kgs7CVqXiO1qbtZa9gnYHUoVApkoxRE7mrXHfg==} + '@cspell/dict-flutter@1.1.0': + resolution: {integrity: sha512-3zDeS7zc2p8tr9YH9tfbOEYfopKY/srNsAa+kE3rfBTtQERAZeOhe5yxrnTPoufctXLyuUtcGMUTpxr3dO0iaA==} - '@cspell/dict-fonts@4.0.3': - resolution: {integrity: sha512-sPd17kV5qgYXLteuHFPn5mbp/oCHKgitNfsZLFC3W2fWEgZlhg4hK+UGig3KzrYhhvQ8wBnmZrAQm0TFKCKzsA==} + '@cspell/dict-fonts@4.0.4': + resolution: {integrity: sha512-cHFho4hjojBcHl6qxidl9CvUb492IuSk7xIf2G2wJzcHwGaCFa2o3gRcxmIg1j62guetAeDDFELizDaJlVRIOg==} - '@cspell/dict-fsharp@1.0.4': - resolution: {integrity: sha512-G5wk0o1qyHUNi9nVgdE1h5wl5ylq7pcBjX8vhjHcO4XBq20D5eMoXjwqMo/+szKAqzJ+WV3BgAL50akLKrT9Rw==} + '@cspell/dict-fsharp@1.1.0': + resolution: {integrity: sha512-oguWmHhGzgbgbEIBKtgKPrFSVAFtvGHaQS0oj+vacZqMObwkapcTGu7iwf4V3Bc2T3caf0QE6f6rQfIJFIAVsw==} '@cspell/dict-fullstack@3.2.3': resolution: {integrity: sha512-62PbndIyQPH11mAv0PyiyT0vbwD0AXEocPpHlCHzfb5v9SspzCCbzQ/LIBiFmyRa+q5LMW35CnSVu6OXdT+LKg==} - '@cspell/dict-gaming-terms@1.0.9': - resolution: {integrity: sha512-AVIrZt3YiUnxsUzzGYTZ1XqgtkgwGEO0LWIlEf+SiDUEVLtv4CYmmyXFQ+WXDN0pyJ0wOwDazWrP0Cu7avYQmQ==} + '@cspell/dict-gaming-terms@1.1.0': + resolution: {integrity: sha512-46AnDs9XkgJ2f1Sqol1WgfJ8gOqp60fojpc9Wxch7x+BA63g4JfMV5/M5x0sI0TLlLY8EBSglcr8wQF/7C80AQ==} - '@cspell/dict-git@3.0.3': - resolution: {integrity: sha512-LSxB+psZ0qoj83GkyjeEH/ZViyVsGEF/A6BAo8Nqc0w0HjD2qX/QR4sfA6JHUgQ3Yi/ccxdK7xNIo67L2ScW5A==} + '@cspell/dict-git@3.0.4': + resolution: {integrity: sha512-C44M+m56rYn6QCsLbiKiedyPTMZxlDdEYAsPwwlL5bhMDDzXZ3Ic8OCQIhMbiunhCOJJT+er4URmOmM+sllnjg==} - '@cspell/dict-golang@6.0.17': - resolution: {integrity: sha512-uDDLEJ/cHdLiqPw4+5BnmIo2i/TSR+uDvYd6JlBjTmjBKpOCyvUgYRztH7nv5e7virsN5WDiUWah4/ATQGz4Pw==} + '@cspell/dict-golang@6.0.18': + resolution: {integrity: sha512-Mt+7NwfodDwUk7423DdaQa0YaA+4UoV3XSxQwZioqjpFBCuxfvvv4l80MxCTAAbK6duGj0uHbGTwpv8fyKYPKg==} - '@cspell/dict-google@1.0.4': - resolution: {integrity: sha512-JThUT9eiguCja1mHHLwYESgxkhk17Gv7P3b1S7ZJzXw86QyVHPrbpVoMpozHk0C9o+Ym764B7gZGKmw9uMGduQ==} + '@cspell/dict-google@1.0.5': + resolution: {integrity: sha512-KNrzfUsoFat94slWzo36g601sIGz6KtE4kBMM0gpqwFLK/MXRyaW65IL4SwysY0PEhuRzg9spLLMnUXuVcY2hQ==} - '@cspell/dict-haskell@4.0.4': - resolution: {integrity: sha512-EwQsedEEnND/vY6tqRfg9y7tsnZdxNqOxLXSXTsFA6JRhUlr8Qs88iUUAfsUzWc4nNmmzQH2UbtT25ooG9x4nA==} + '@cspell/dict-haskell@4.0.5': + resolution: {integrity: sha512-s4BG/4tlj2pPM9Ha7IZYMhUujXDnI0Eq1+38UTTCpatYLbQqDwRFf2KNPLRqkroU+a44yTUAe0rkkKbwy4yRtQ==} '@cspell/dict-html-symbol-entities@4.0.3': resolution: {integrity: sha512-aABXX7dMLNFdSE8aY844X4+hvfK7977sOWgZXo4MTGAmOzR8524fjbJPswIBK7GaD3+SgFZ2yP2o0CFvXDGF+A==} - '@cspell/dict-html@4.0.10': - resolution: {integrity: sha512-I9uRAcdtHbh0wEtYZlgF0TTcgH0xaw1B54G2CW+tx4vHUwlde/+JBOfIzird4+WcMv4smZOfw+qHf7puFUbI5g==} + '@cspell/dict-html@4.0.11': + resolution: {integrity: sha512-QR3b/PB972SRQ2xICR1Nw/M44IJ6rjypwzA4jn+GH8ydjAX9acFNfc+hLZVyNe0FqsE90Gw3evLCOIF0vy1vQw==} - '@cspell/dict-java@5.0.10': - resolution: {integrity: sha512-pVNcOnmoGiNL8GSVq4WbX/Vs2FGS0Nej+1aEeGuUY9CU14X8yAVCG+oih5ZoLt1jaR8YfR8byUF8wdp4qG4XIw==} + '@cspell/dict-java@5.0.11': + resolution: {integrity: sha512-T4t/1JqeH33Raa/QK/eQe26FE17eUCtWu+JsYcTLkQTci2dk1DfcIKo8YVHvZXBnuM43ATns9Xs0s+AlqDeH7w==} - '@cspell/dict-julia@1.0.4': - resolution: {integrity: sha512-bFVgNX35MD3kZRbXbJVzdnN7OuEqmQXGpdOi9jzB40TSgBTlJWA4nxeAKV4CPCZxNRUGnLH0p05T/AD7Aom9/w==} + '@cspell/dict-julia@1.1.0': + resolution: {integrity: sha512-CPUiesiXwy3HRoBR3joUseTZ9giFPCydSKu2rkh6I2nVjXnl5vFHzOMLXpbF4HQ1tH2CNfnDbUndxD+I+7eL9w==} - '@cspell/dict-k8s@1.0.9': - resolution: {integrity: sha512-Q7GELSQIzo+BERl2ya/nBEnZeQC+zJP19SN1pI6gqDYraM51uYJacbbcWLYYO2Y+5joDjNt/sd/lJtLaQwoSlA==} + '@cspell/dict-k8s@1.0.10': + resolution: {integrity: sha512-313haTrX9prep1yWO7N6Xw4D6tvUJ0Xsx+YhCP+5YrrcIKoEw5Rtlg8R4PPzLqe6zibw6aJ+Eqq+y76Vx5BZkw==} + + '@cspell/dict-kotlin@1.1.0': + resolution: {integrity: sha512-vySaVw6atY7LdwvstQowSbdxjXG6jDhjkWVWSjg1XsUckyzH1JRHXe9VahZz1i7dpoFEUOWQrhIe5B9482UyJQ==} '@cspell/dict-latex@4.0.3': resolution: {integrity: sha512-2KXBt9fSpymYHxHfvhUpjUFyzrmN4c4P8mwIzweLyvqntBT3k0YGZJSriOdjfUjwSygrfEwiuPI1EMrvgrOMJw==} - '@cspell/dict-lorem-ipsum@4.0.3': - resolution: {integrity: sha512-WFpDi/PDYHXft6p0eCXuYnn7mzMEQLVeqpO+wHSUd+kz5ADusZ4cpslAA4wUZJstF1/1kMCQCZM6HLZic9bT8A==} + '@cspell/dict-lorem-ipsum@4.0.4': + resolution: {integrity: sha512-+4f7vtY4dp2b9N5fn0za/UR0kwFq2zDtA62JCbWHbpjvO9wukkbl4rZg4YudHbBgkl73HRnXFgCiwNhdIA1JPw==} - '@cspell/dict-lua@4.0.6': - resolution: {integrity: sha512-Jwvh1jmAd9b+SP9e1GkS2ACbqKKRo9E1f9GdjF/ijmooZuHU0hPyqvnhZzUAxO1egbnNjxS/J2T6iUtjAUK2KQ==} + '@cspell/dict-lua@4.0.7': + resolution: {integrity: sha512-Wbr7YSQw+cLHhTYTKV6cAljgMgcY+EUAxVIZW3ljKswEe4OLxnVJ7lPqZF5JKjlXdgCjbPSimsHqyAbC5pQN/Q==} - '@cspell/dict-makefile@1.0.3': - resolution: {integrity: sha512-R3U0DSpvTs6qdqfyBATnePj9Q/pypkje0Nj26mQJ8TOBQutCRAJbr2ZFAeDjgRx5EAJU/+8txiyVF97fbVRViw==} + '@cspell/dict-makefile@1.0.4': + resolution: {integrity: sha512-E4hG/c0ekPqUBvlkrVvzSoAA+SsDA9bLi4xSV3AXHTVru7Y2bVVGMPtpfF+fI3zTkww/jwinprcU1LSohI3ylw==} - '@cspell/dict-markdown@2.0.7': - resolution: {integrity: sha512-F9SGsSOokFn976DV4u/1eL4FtKQDSgJHSZ3+haPRU5ki6OEqojxKa8hhj4AUrtNFpmBaJx/WJ4YaEzWqG7hgqg==} + '@cspell/dict-markdown@2.0.9': + resolution: {integrity: sha512-j2e6Eg18BlTb1mMP1DkyRFMM/FLS7qiZjltpURzDckB57zDZbUyskOFdl4VX7jItZZEeY0fe22bSPOycgS1Z5A==} peerDependencies: - '@cspell/dict-css': ^4.0.16 - '@cspell/dict-html': ^4.0.10 + '@cspell/dict-css': ^4.0.17 + '@cspell/dict-html': ^4.0.11 '@cspell/dict-html-symbol-entities': ^4.0.3 - '@cspell/dict-typescript': ^3.1.11 + '@cspell/dict-typescript': ^3.2.0 - '@cspell/dict-monkeyc@1.0.9': - resolution: {integrity: sha512-Jvf6g5xlB4+za3ThvenYKREXTEgzx5gMUSzrAxIiPleVG4hmRb/GBSoSjtkGaibN3XxGx5x809gSTYCA/IHCpA==} + '@cspell/dict-monkeyc@1.0.10': + resolution: {integrity: sha512-7RTGyKsTIIVqzbvOtAu6Z/lwwxjGRtY5RkKPlXKHEoEAgIXwfDxb5EkVwzGQwQr8hF/D3HrdYbRT8MFBfsueZw==} - '@cspell/dict-node@5.0.5': - resolution: {integrity: sha512-7NbCS2E8ZZRZwlLrh2sA0vAk9n1kcTUiRp/Nia8YvKaItGXLfxYqD2rMQ3HpB1kEutal6hQLVic3N2Yi1X7AaA==} + '@cspell/dict-node@5.0.6': + resolution: {integrity: sha512-CEbhPCpxGvRNByGolSBTrXXW2rJA4bGqZuTx1KKO85mwR6aadeOmUE7xf/8jiCkXSy+qvr9aJeh+jlfXcsrziQ==} - '@cspell/dict-npm@5.1.18': - resolution: {integrity: sha512-/Nukl+DSxtEWSlb8svWFSpJVctAsM9SP+f5Q1n+qdDcXNKMb1bUCo/d3QZPwyOhuMjDawnsGBUAfp+iq7Mw83Q==} + '@cspell/dict-npm@5.1.22': + resolution: {integrity: sha512-fZBTn8QHr8pAv1/I14CmdDWpVkovCfYpSYiGfV1SZkOjrsKLzPxsP84eaP3RijbFtYj3GMplVN27FR3H5oHfiw==} - '@cspell/dict-php@4.0.13': - resolution: {integrity: sha512-P6sREMZkhElzz/HhXAjahnICYIqB/HSGp1EhZh+Y6IhvC15AzgtDP8B8VYCIsQof6rPF1SQrFwunxOv8H1e2eg==} + '@cspell/dict-php@4.0.14': + resolution: {integrity: sha512-7zur8pyncYZglxNmqsRycOZ6inpDoVd4yFfz1pQRe5xaRWMiK3Km4n0/X/1YMWhh3e3Sl/fQg5Axb2hlN68t1g==} - '@cspell/dict-powershell@5.0.13': - resolution: {integrity: sha512-0qdj0XZIPmb77nRTynKidRJKTU0Fl+10jyLbAhFTuBWKMypVY06EaYFnwhsgsws/7nNX8MTEQuewbl9bWFAbsg==} + '@cspell/dict-powershell@5.0.14': + resolution: {integrity: sha512-ktjjvtkIUIYmj/SoGBYbr3/+CsRGNXGpvVANrY0wlm/IoGlGywhoTUDYN0IsGwI2b8Vktx3DZmQkfb3Wo38jBA==} - '@cspell/dict-public-licenses@2.0.11': - resolution: {integrity: sha512-rR5KjRUSnVKdfs5G+gJ4oIvQvm8+NJ6cHWY2N+GE69/FSGWDOPHxulCzeGnQU/c6WWZMSimG9o49i9r//lUQyA==} + '@cspell/dict-public-licenses@2.0.12': + resolution: {integrity: sha512-obreJMVbz8ZrXyc60PcS/B2FwXaO3AWPO2x50zrI/n4UDuBr/UdPb6M1q++6c08n+151I35GEx52xRFiToSg4g==} - '@cspell/dict-python@4.2.13': - resolution: {integrity: sha512-mZIcmo9qif8LkJ6N/lqTZawcOk2kVTcuWIUOSbMcjyomO0XZ7iWz15TfONyr03Ea/l7o5ULV+MZ4vx76bAUb7w==} + '@cspell/dict-python@4.2.14': + resolution: {integrity: sha512-NZ/rsTH5gqTlEwbSg0vn5b1TsyzrUvA6ykwCVCwsVDdlQAS82cyDsF9JqHp8S4d6PFykmkfSxtAXYyOUr0KCbg==} - '@cspell/dict-r@2.0.4': - resolution: {integrity: sha512-cBpRsE/U0d9BRhiNRMLMH1PpWgw+N+1A2jumgt1if9nBGmQw4MUpg2u9I0xlFVhstTIdzXiLXMxP45cABuiUeQ==} + '@cspell/dict-r@2.1.0': + resolution: {integrity: sha512-k2512wgGG0lTpTYH9w5Wwco+lAMf3Vz7mhqV8+OnalIE7muA0RSuD9tWBjiqLcX8zPvEJr4LdgxVju8Gk3OKyA==} '@cspell/dict-ruby@5.0.7': resolution: {integrity: sha512-4/d0hcoPzi5Alk0FmcyqlzFW9lQnZh9j07MJzPcyVO62nYJJAGKaPZL2o4qHeCS/od/ctJC5AHRdoUm0ktsw6Q==} - '@cspell/dict-rust@4.0.10': - resolution: {integrity: sha512-6o5C8566VGTTctgcwfF3Iy7314W0oMlFFSQOadQ0OEdJ9Z9ERX/PDimrzP3LGuOrvhtEFoK8pj+BLnunNwRNrw==} + '@cspell/dict-rust@4.0.11': + resolution: {integrity: sha512-OGWDEEzm8HlkSmtD8fV3pEcO2XBpzG2XYjgMCJCRwb2gRKvR+XIm6Dlhs04N/K2kU+iH8bvrqNpM8fS/BFl0uw==} + + '@cspell/dict-scala@5.0.7': + resolution: {integrity: sha512-yatpSDW/GwulzO3t7hB5peoWwzo+Y3qTc0pO24Jf6f88jsEeKmDeKkfgPbYuCgbE4jisGR4vs4+jfQZDIYmXPA==} - '@cspell/dict-scala@5.0.6': - resolution: {integrity: sha512-tl0YWAfjUVb4LyyE4JIMVE8DlLzb1ecHRmIWc4eT6nkyDqQgHKzdHsnusxFEFMVLIQomgSg0Zz6hJ5S1E4W4ww==} + '@cspell/dict-shell@1.1.0': + resolution: {integrity: sha512-D/xHXX7T37BJxNRf5JJHsvziFDvh23IF/KvkZXNSh8VqcRdod3BAz9VGHZf6VDqcZXr1VRqIYR3mQ8DSvs3AVQ==} - '@cspell/dict-software-terms@4.1.20': - resolution: {integrity: sha512-ma51njqbk9ZKzZF9NpCZpZ+c50EwR5JTJ2LEXlX0tX+ExVbKpthhlDLhT2+mkUh5Zvj+CLf5F9z0qB4+X3re/w==} + '@cspell/dict-software-terms@4.2.2': + resolution: {integrity: sha512-cgteXRzx2W/Ug7QSdFJrVxLES7krrZEjZ9J6sXRWOsVYFpgu2Gup8NKmjKOZ8NTnCjHQFrMnbmKdv56q9Kwixw==} - '@cspell/dict-sql@2.1.8': - resolution: {integrity: sha512-dJRE4JV1qmXTbbGm6WIcg1knmR6K5RXnQxF4XHs5HA3LAjc/zf77F95i5LC+guOGppVF6Hdl66S2UyxT+SAF3A==} + '@cspell/dict-sql@2.2.0': + resolution: {integrity: sha512-MUop+d1AHSzXpBvQgQkCiok8Ejzb+nrzyG16E8TvKL2MQeDwnIvMe3bv90eukP6E1HWb+V/MA/4pnq0pcJWKqQ==} - '@cspell/dict-svelte@1.0.5': - resolution: {integrity: sha512-sseHlcXOqWE4Ner9sg8KsjxwSJ2yssoJNqFHR9liWVbDV+m7kBiUtn2EB690TihzVsEmDr/0Yxrbb5Bniz70mA==} + '@cspell/dict-svelte@1.0.6': + resolution: {integrity: sha512-8LAJHSBdwHCoKCSy72PXXzz7ulGROD0rP1CQ0StOqXOOlTUeSFaJJlxNYjlONgd2c62XBQiN2wgLhtPN+1Zv7Q==} - '@cspell/dict-swift@2.0.4': - resolution: {integrity: sha512-CsFF0IFAbRtYNg0yZcdaYbADF5F3DsM8C4wHnZefQy8YcHP/qjAF/GdGfBFBLx+XSthYuBlo2b2XQVdz3cJZBw==} + '@cspell/dict-swift@2.0.5': + resolution: {integrity: sha512-3lGzDCwUmnrfckv3Q4eVSW3sK3cHqqHlPprFJZD4nAqt23ot7fic5ALR7J4joHpvDz36nHX34TgcbZNNZOC/JA==} - '@cspell/dict-terraform@1.0.6': - resolution: {integrity: sha512-Sqm5vGbXuI9hCFcr4w6xWf4Y25J9SdleE/IqfM6RySPnk8lISEmVdax4k6+Kinv9qaxyvnIbUUN4WFLWcBPQAg==} + '@cspell/dict-terraform@1.1.0': + resolution: {integrity: sha512-G55pcUUxeXAhejstmD35B47SkFd4uqCQimc+CMgq8Nx0dr03guL2iMsz8faRWQGkCnGimX8S91rbOhDv9p/heg==} - '@cspell/dict-typescript@3.1.11': - resolution: {integrity: sha512-FwvK5sKbwrVpdw0e9+1lVTl8FPoHYvfHRuQRQz2Ql5XkC0gwPPkpoyD1zYImjIyZRoYXk3yp9j8ss4iz7A7zoQ==} + '@cspell/dict-typescript@3.2.0': + resolution: {integrity: sha512-Pk3zNePLT8qg51l0M4g1ISowYAEGxTuNfZlgkU5SvHa9Cu7x/BWoyYq9Fvc3kAyoisCjRPyvWF4uRYrPitPDFw==} - '@cspell/dict-vue@3.0.3': - resolution: {integrity: sha512-akmYbrgAGumqk1xXALtDJcEcOMYBYMnkjpmGzH13Ozhq1mkPF4VgllFQlm1xYde+BUKNnzMgPEzxrL2qZllgYA==} + '@cspell/dict-vue@3.0.4': + resolution: {integrity: sha512-0dPtI0lwHcAgSiQFx8CzvqjdoXROcH+1LyqgROCpBgppommWpVhbQ0eubnKotFEXgpUCONVkeZJ6Ql8NbTEu+w==} - '@cspell/dynamic-import@8.17.1': - resolution: {integrity: sha512-XQtr2olYOtqbg49E+8SISd6I5DzfxmsKINDn0ZgaTFeLalnNdF3ewDU4gOEbApIzGffRa1mW9t19MsiVrznSDw==} + '@cspell/dynamic-import@8.17.2': + resolution: {integrity: sha512-n3AVbyBlTn/pLtYK62mqgDfJIuQHUTY/k8SMUCjyjfgoqd3LcKhS1PmbLfDWPMTODK30cSMMTLejjy2bL6ksEw==} engines: {node: '>=18.0'} - '@cspell/filetypes@8.17.1': - resolution: {integrity: sha512-AxYw6j7EPYtDFAFjwybjFpMc9waXQzurfBXmEVfQ5RQRlbylujLZWwR6GnMqofeNg4oGDUpEjcAZFrgdkvMQlA==} + '@cspell/filetypes@8.17.2': + resolution: {integrity: sha512-2B+dB4Ls2xiOjg+vEEbAuJTHtMfXSihVzfLGnj9+qUfq47iqrz4ZBvCOfZhYdiVaaZJoZUgIw8ljrUfqFzYDAg==} engines: {node: '>=18'} - '@cspell/strong-weak-map@8.17.1': - resolution: {integrity: sha512-8cY3vLAKdt5gQEMM3Gr57BuQ8sun2NjYNh9qTdrctC1S9gNC7XzFghTYAfHSWR4VrOUcMFLO/izMdsc1KFvFOA==} + '@cspell/strong-weak-map@8.17.2': + resolution: {integrity: sha512-LbbhdVwtqyJ71X+O7e2PqpDp7zLiY8jmW2CJFLjZYWTUawgav2bpwECGq6O9Gnwqe+fj7yWxGJFDSpXQcCJQAw==} engines: {node: '>=18'} - '@cspell/url@8.17.1': - resolution: {integrity: sha512-LMvReIndW1ckvemElfDgTt282fb2C3C/ZXfsm0pJsTV5ZmtdelCHwzmgSBmY5fDr7D66XDp8EurotSE0K6BTvw==} + '@cspell/url@8.17.2': + resolution: {integrity: sha512-yy4eYWNX2iutXmy4Igbn/hL/NYaNt94DylohPtgVr0Zxnn/AAArt9Bv1KXPpjB8VFy2wzzPzWmZ+MWDUVpHCbg==} engines: {node: '>=18.0'} '@es-joy/jsdoccomment@0.49.0': @@ -1181,6 +1196,11 @@ packages: resolution: {integrity: sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==} engines: {node: '>=12'} + '@prettier/sync@0.5.2': + resolution: {integrity: sha512-Yb569su456XNx5BsH/Vyem7xD6g/y9iLmLUzRKM1a/dhU/D7HqqvkAG72znulXlMXztbV0iiu9O5AL8K98TzZQ==} + peerDependencies: + prettier: '*' + '@release-it/conventional-changelog@9.0.3': resolution: {integrity: sha512-+3TL+B89Kc+VTbfGxpTvJkbegWt5XIzkovsYVJyoZpOZDG07v25FU8c5R5Q8yNUs76Ikfq0sp+ZTTxmefG4Hiw==} engines: {node: ^18.18.0 || ^20.9.0 || ^22.0.0} @@ -1329,6 +1349,9 @@ packages: '@types/katex@0.16.7': resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==} + '@types/lodash@4.17.14': + resolution: {integrity: sha512-jsxagdikDiDBeIRaPYtArcT8my4tN1og7MtMRquFT3XNA6axxyHDRUemqDz/taRDdOUn0GnGHRCuff4q48sW9A==} + '@types/mdast@3.0.10': resolution: {integrity: sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==} @@ -1765,6 +1788,10 @@ packages: resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} engines: {node: '>=18'} + commander@13.0.0: + resolution: {integrity: sha512-oPYleIY8wmTVzkvQq10AEok6YcTC4sRUBl8F9gVuwchGVUCTbl/vhLTaQqutuuySYOsu8YTgV+OxKc/8Yvx+mQ==} + engines: {node: '>=18'} + commander@4.1.1: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} @@ -1900,34 +1927,34 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} - cspell-config-lib@8.17.1: - resolution: {integrity: sha512-x1S7QWprgUcwuwiJB1Ng0ZTBC4G50qP9qQyg/aroMkcdMsHfk26E8jUGRPNt4ftHFzS4YMhwtXuJQ9IgRUuNPA==} + cspell-config-lib@8.17.2: + resolution: {integrity: sha512-g08lRd/smLk2je0j7HlCjdDa0dSTyI2oRP3gScWlsyXjb4NSr9qO0Wzyn5BfPgrqFdS/z4dXbHe+tnLQZCt9iQ==} engines: {node: '>=18'} - cspell-dictionary@8.17.1: - resolution: {integrity: sha512-zSl9l3wii+x16yc2NVZl/+CMLeLBAiuEd5YoFkOYPcbTJnfPwdjMNcj71u7wBvNJ+qwbF+kGbutEt15yHW3NBw==} + cspell-dictionary@8.17.2: + resolution: {integrity: sha512-2JC9RRsZruCs3AHId/8X63fSxDoF94dleRp8y/dXS9LIX7NruofohUJwzc/3tlgzCWWdaek1RXhO5xaYX74QtA==} engines: {node: '>=18'} - cspell-gitignore@8.17.1: - resolution: {integrity: sha512-bk727Zf4FBCjm9Mwvyreyhgjwe+YhPQEW7PldkHiinKd+Irfez4s8GXLQb1EgV0UpvViqaqBqLmngjZdS30BTA==} + cspell-gitignore@8.17.2: + resolution: {integrity: sha512-zCTTN30zV96LkZmUDrLamEHpLLUGohKglKJ4iXoHQC8pDU3xTsV2qzeCQjM9SEmU3VbE1TzWq+vj0fslasv6pA==} engines: {node: '>=18'} hasBin: true - cspell-glob@8.17.1: - resolution: {integrity: sha512-cUwM5auSt0RvLX7UkP2GEArJRWc85l51B1voArl+3ZIKeMZwcJpJgN3qvImtF8yRTZwYeYCs1sgsihb179q+mg==} + cspell-glob@8.17.2: + resolution: {integrity: sha512-MTgrWX12oY8Pq/M3PEYCTHwD6w6l+DPtBWm958nhR4dboUbwi/3KfqCtdorkhnuClqLDQuuZHp0uGBXB4cdQrw==} engines: {node: '>=18'} - cspell-grammar@8.17.1: - resolution: {integrity: sha512-H5tLcBuW7aUj9L0rR+FSbnWPEsWb8lWppHVidtqw9Ll1CUHWOZC9HTB2RdrhJZrsz/8DJbM2yNbok0Xt0VAfdw==} + cspell-grammar@8.17.2: + resolution: {integrity: sha512-Asg5XRvrg2yHCvBwzARBPSwI4P5/unN+bKBlxqFazHgR72WJE+ASeobfUNfGi/RxJA2+m0hO91oYtvq6LfK52w==} engines: {node: '>=18'} hasBin: true - cspell-io@8.17.1: - resolution: {integrity: sha512-liIOsblt7oVItifzRAbuxiYrwlgw1VOqKppMxVKtYoAn2VUuuEpjCj6jLWpoTqSszR/38o7ChsHY1LHakhJZmw==} + cspell-io@8.17.2: + resolution: {integrity: sha512-IUdhbO6gsWYiM2dgudFJQTfnFCDYjLOqal3SxH5o8oOWeu5iIZ+s3N8E1odz0L5zF2Go7zDQSKvPr7Y9OOoRfw==} engines: {node: '>=18'} - cspell-lib@8.17.1: - resolution: {integrity: sha512-66n83Q7bK5tnvkDH7869/pBY/65AKmZVfCOAlsbhJn3YMDbNHFCHR0d1oNMlqG+n65Aco89VGwYfXxImZY+/mA==} + cspell-lib@8.17.2: + resolution: {integrity: sha512-ZgkTvGh9FO+R3v5TaTqlrJEylWyZhNOzbtrQ5W35Hb3tZ9IJJklxjlcGe+gbFsjGi56kLj6c5L2NR7YX/Fdu5Q==} engines: {node: '>=18'} cspell-populate-words@0.3.0: @@ -1935,12 +1962,12 @@ packages: engines: {node: '>=18.3.0'} hasBin: true - cspell-trie-lib@8.17.1: - resolution: {integrity: sha512-13WNa5s75VwOjlGzWprmfNbBFIfXyA7tYYrbV+LugKkznyNZJeJPojHouEudcLq3SYb2Q6tJ7qyWcuT5bR9qPA==} + cspell-trie-lib@8.17.2: + resolution: {integrity: sha512-Bw9q8EWFihkQGo8fNdfkUqYOTsC161+wrQxR7m74K4bKEmQgm0mS0sLHKUwxEOZVGGLmIw9dMQl+8WnTgqOaMQ==} engines: {node: '>=18'} - cspell@8.17.1: - resolution: {integrity: sha512-D0lw8XTXrTycNzOn5DkfPJNUT00X53OgvFDm+0SzhBr1r+na8LEh3CnQ6zKYVU0fL0x8vU82vs4jmGjDho9mPg==} + cspell@8.17.2: + resolution: {integrity: sha512-y+INkxDa+M9f+gsyyMLjKh1tF20r2g5Gn22peSRJglrNLQtmDuRtDT9vyDHANXZcH5g6pHDnENQu/+P2Tiyu8Q==} engines: {node: '>=18'} hasBin: true @@ -2306,8 +2333,8 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - fast-equals@5.0.1: - resolution: {integrity: sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==} + fast-equals@5.2.2: + resolution: {integrity: sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw==} engines: {node: '>=6.0.0'} fast-glob@3.3.2: @@ -3029,6 +3056,9 @@ packages: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} + make-synchronized@0.2.9: + resolution: {integrity: sha512-4wczOs8SLuEdpEvp3vGo83wh8rjJ78UsIk7DIX5fxdfmfMJGog4bQzxfvOwq7Q3yCHLC4jp1urPHIxRS/A93gA==} + markdown-it@14.1.0: resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} hasBin: true @@ -4289,6 +4319,11 @@ packages: engines: {node: '>= 14'} hasBin: true + yaml@2.7.0: + resolution: {integrity: sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==} + engines: {node: '>= 14'} + hasBin: true + yargs-parser@18.1.3: resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} engines: {node: '>=6'} @@ -4413,207 +4448,216 @@ snapshots: conventional-commits-filter: 5.0.0 conventional-commits-parser: 6.0.0 - '@cspell/cspell-bundled-dicts@8.17.1': - dependencies: - '@cspell/dict-ada': 4.0.5 - '@cspell/dict-al': 1.0.3 - '@cspell/dict-aws': 4.0.7 - '@cspell/dict-bash': 4.1.8 - '@cspell/dict-companies': 3.1.9 - '@cspell/dict-cpp': 6.0.2 - '@cspell/dict-cryptocurrencies': 5.0.3 - '@cspell/dict-csharp': 4.0.5 - '@cspell/dict-css': 4.0.16 - '@cspell/dict-dart': 2.2.4 - '@cspell/dict-django': 4.1.3 - '@cspell/dict-docker': 1.1.11 - '@cspell/dict-dotnet': 5.0.8 - '@cspell/dict-elixir': 4.0.6 - '@cspell/dict-en-common-misspellings': 2.0.7 + '@cspell/cspell-bundled-dicts@8.17.2': + dependencies: + '@cspell/dict-ada': 4.1.0 + '@cspell/dict-al': 1.1.0 + '@cspell/dict-aws': 4.0.9 + '@cspell/dict-bash': 4.2.0 + '@cspell/dict-companies': 3.1.12 + '@cspell/dict-cpp': 6.0.3 + '@cspell/dict-cryptocurrencies': 5.0.4 + '@cspell/dict-csharp': 4.0.6 + '@cspell/dict-css': 4.0.17 + '@cspell/dict-dart': 2.3.0 + '@cspell/dict-data-science': 2.0.6 + '@cspell/dict-django': 4.1.4 + '@cspell/dict-docker': 1.1.12 + '@cspell/dict-dotnet': 5.0.9 + '@cspell/dict-elixir': 4.0.7 + '@cspell/dict-en-common-misspellings': 2.0.8 '@cspell/dict-en-gb': 1.1.33 - '@cspell/dict-en_us': 4.3.28 - '@cspell/dict-filetypes': 3.0.9 - '@cspell/dict-flutter': 1.0.3 - '@cspell/dict-fonts': 4.0.3 - '@cspell/dict-fsharp': 1.0.4 + '@cspell/dict-en_us': 4.3.29 + '@cspell/dict-filetypes': 3.0.10 + '@cspell/dict-flutter': 1.1.0 + '@cspell/dict-fonts': 4.0.4 + '@cspell/dict-fsharp': 1.1.0 '@cspell/dict-fullstack': 3.2.3 - '@cspell/dict-gaming-terms': 1.0.9 - '@cspell/dict-git': 3.0.3 - '@cspell/dict-golang': 6.0.17 - '@cspell/dict-google': 1.0.4 - '@cspell/dict-haskell': 4.0.4 - '@cspell/dict-html': 4.0.10 + '@cspell/dict-gaming-terms': 1.1.0 + '@cspell/dict-git': 3.0.4 + '@cspell/dict-golang': 6.0.18 + '@cspell/dict-google': 1.0.5 + '@cspell/dict-haskell': 4.0.5 + '@cspell/dict-html': 4.0.11 '@cspell/dict-html-symbol-entities': 4.0.3 - '@cspell/dict-java': 5.0.10 - '@cspell/dict-julia': 1.0.4 - '@cspell/dict-k8s': 1.0.9 + '@cspell/dict-java': 5.0.11 + '@cspell/dict-julia': 1.1.0 + '@cspell/dict-k8s': 1.0.10 + '@cspell/dict-kotlin': 1.1.0 '@cspell/dict-latex': 4.0.3 - '@cspell/dict-lorem-ipsum': 4.0.3 - '@cspell/dict-lua': 4.0.6 - '@cspell/dict-makefile': 1.0.3 - '@cspell/dict-markdown': 2.0.7(@cspell/dict-css@4.0.16)(@cspell/dict-html-symbol-entities@4.0.3)(@cspell/dict-html@4.0.10)(@cspell/dict-typescript@3.1.11) - '@cspell/dict-monkeyc': 1.0.9 - '@cspell/dict-node': 5.0.5 - '@cspell/dict-npm': 5.1.18 - '@cspell/dict-php': 4.0.13 - '@cspell/dict-powershell': 5.0.13 - '@cspell/dict-public-licenses': 2.0.11 - '@cspell/dict-python': 4.2.13 - '@cspell/dict-r': 2.0.4 + '@cspell/dict-lorem-ipsum': 4.0.4 + '@cspell/dict-lua': 4.0.7 + '@cspell/dict-makefile': 1.0.4 + '@cspell/dict-markdown': 2.0.9(@cspell/dict-css@4.0.17)(@cspell/dict-html-symbol-entities@4.0.3)(@cspell/dict-html@4.0.11)(@cspell/dict-typescript@3.2.0) + '@cspell/dict-monkeyc': 1.0.10 + '@cspell/dict-node': 5.0.6 + '@cspell/dict-npm': 5.1.22 + '@cspell/dict-php': 4.0.14 + '@cspell/dict-powershell': 5.0.14 + '@cspell/dict-public-licenses': 2.0.12 + '@cspell/dict-python': 4.2.14 + '@cspell/dict-r': 2.1.0 '@cspell/dict-ruby': 5.0.7 - '@cspell/dict-rust': 4.0.10 - '@cspell/dict-scala': 5.0.6 - '@cspell/dict-software-terms': 4.1.20 - '@cspell/dict-sql': 2.1.8 - '@cspell/dict-svelte': 1.0.5 - '@cspell/dict-swift': 2.0.4 - '@cspell/dict-terraform': 1.0.6 - '@cspell/dict-typescript': 3.1.11 - '@cspell/dict-vue': 3.0.3 + '@cspell/dict-rust': 4.0.11 + '@cspell/dict-scala': 5.0.7 + '@cspell/dict-shell': 1.1.0 + '@cspell/dict-software-terms': 4.2.2 + '@cspell/dict-sql': 2.2.0 + '@cspell/dict-svelte': 1.0.6 + '@cspell/dict-swift': 2.0.5 + '@cspell/dict-terraform': 1.1.0 + '@cspell/dict-typescript': 3.2.0 + '@cspell/dict-vue': 3.0.4 - '@cspell/cspell-json-reporter@8.17.1': + '@cspell/cspell-json-reporter@8.17.2': dependencies: - '@cspell/cspell-types': 8.17.1 + '@cspell/cspell-types': 8.17.2 - '@cspell/cspell-pipe@8.17.1': {} + '@cspell/cspell-pipe@8.17.2': {} - '@cspell/cspell-resolver@8.17.1': + '@cspell/cspell-resolver@8.17.2': dependencies: global-directory: 4.0.1 - '@cspell/cspell-service-bus@8.17.1': {} + '@cspell/cspell-service-bus@8.17.2': {} - '@cspell/cspell-types@8.17.1': {} + '@cspell/cspell-types@8.17.2': {} - '@cspell/dict-ada@4.0.5': {} + '@cspell/dict-ada@4.1.0': {} - '@cspell/dict-al@1.0.3': {} + '@cspell/dict-al@1.1.0': {} - '@cspell/dict-aws@4.0.7': {} + '@cspell/dict-aws@4.0.9': {} - '@cspell/dict-bash@4.1.8': {} + '@cspell/dict-bash@4.2.0': + dependencies: + '@cspell/dict-shell': 1.1.0 - '@cspell/dict-companies@3.1.9': {} + '@cspell/dict-companies@3.1.12': {} - '@cspell/dict-cpp@6.0.2': {} + '@cspell/dict-cpp@6.0.3': {} - '@cspell/dict-cryptocurrencies@5.0.3': {} + '@cspell/dict-cryptocurrencies@5.0.4': {} - '@cspell/dict-csharp@4.0.5': {} + '@cspell/dict-csharp@4.0.6': {} - '@cspell/dict-css@4.0.16': {} + '@cspell/dict-css@4.0.17': {} - '@cspell/dict-dart@2.2.4': {} + '@cspell/dict-dart@2.3.0': {} - '@cspell/dict-data-science@2.0.5': {} + '@cspell/dict-data-science@2.0.6': {} - '@cspell/dict-django@4.1.3': {} + '@cspell/dict-django@4.1.4': {} - '@cspell/dict-docker@1.1.11': {} + '@cspell/dict-docker@1.1.12': {} - '@cspell/dict-dotnet@5.0.8': {} + '@cspell/dict-dotnet@5.0.9': {} - '@cspell/dict-elixir@4.0.6': {} + '@cspell/dict-elixir@4.0.7': {} - '@cspell/dict-en-common-misspellings@2.0.7': {} + '@cspell/dict-en-common-misspellings@2.0.8': {} '@cspell/dict-en-gb@1.1.33': {} - '@cspell/dict-en_us@4.3.28': {} + '@cspell/dict-en_us@4.3.29': {} - '@cspell/dict-filetypes@3.0.9': {} + '@cspell/dict-filetypes@3.0.10': {} - '@cspell/dict-flutter@1.0.3': {} + '@cspell/dict-flutter@1.1.0': {} - '@cspell/dict-fonts@4.0.3': {} + '@cspell/dict-fonts@4.0.4': {} - '@cspell/dict-fsharp@1.0.4': {} + '@cspell/dict-fsharp@1.1.0': {} '@cspell/dict-fullstack@3.2.3': {} - '@cspell/dict-gaming-terms@1.0.9': {} + '@cspell/dict-gaming-terms@1.1.0': {} - '@cspell/dict-git@3.0.3': {} + '@cspell/dict-git@3.0.4': {} - '@cspell/dict-golang@6.0.17': {} + '@cspell/dict-golang@6.0.18': {} - '@cspell/dict-google@1.0.4': {} + '@cspell/dict-google@1.0.5': {} - '@cspell/dict-haskell@4.0.4': {} + '@cspell/dict-haskell@4.0.5': {} '@cspell/dict-html-symbol-entities@4.0.3': {} - '@cspell/dict-html@4.0.10': {} + '@cspell/dict-html@4.0.11': {} + + '@cspell/dict-java@5.0.11': {} - '@cspell/dict-java@5.0.10': {} + '@cspell/dict-julia@1.1.0': {} - '@cspell/dict-julia@1.0.4': {} + '@cspell/dict-k8s@1.0.10': {} - '@cspell/dict-k8s@1.0.9': {} + '@cspell/dict-kotlin@1.1.0': {} '@cspell/dict-latex@4.0.3': {} - '@cspell/dict-lorem-ipsum@4.0.3': {} + '@cspell/dict-lorem-ipsum@4.0.4': {} - '@cspell/dict-lua@4.0.6': {} + '@cspell/dict-lua@4.0.7': {} - '@cspell/dict-makefile@1.0.3': {} + '@cspell/dict-makefile@1.0.4': {} - '@cspell/dict-markdown@2.0.7(@cspell/dict-css@4.0.16)(@cspell/dict-html-symbol-entities@4.0.3)(@cspell/dict-html@4.0.10)(@cspell/dict-typescript@3.1.11)': + '@cspell/dict-markdown@2.0.9(@cspell/dict-css@4.0.17)(@cspell/dict-html-symbol-entities@4.0.3)(@cspell/dict-html@4.0.11)(@cspell/dict-typescript@3.2.0)': dependencies: - '@cspell/dict-css': 4.0.16 - '@cspell/dict-html': 4.0.10 + '@cspell/dict-css': 4.0.17 + '@cspell/dict-html': 4.0.11 '@cspell/dict-html-symbol-entities': 4.0.3 - '@cspell/dict-typescript': 3.1.11 + '@cspell/dict-typescript': 3.2.0 - '@cspell/dict-monkeyc@1.0.9': {} + '@cspell/dict-monkeyc@1.0.10': {} - '@cspell/dict-node@5.0.5': {} + '@cspell/dict-node@5.0.6': {} - '@cspell/dict-npm@5.1.18': {} + '@cspell/dict-npm@5.1.22': {} - '@cspell/dict-php@4.0.13': {} + '@cspell/dict-php@4.0.14': {} - '@cspell/dict-powershell@5.0.13': {} + '@cspell/dict-powershell@5.0.14': {} - '@cspell/dict-public-licenses@2.0.11': {} + '@cspell/dict-public-licenses@2.0.12': {} - '@cspell/dict-python@4.2.13': + '@cspell/dict-python@4.2.14': dependencies: - '@cspell/dict-data-science': 2.0.5 + '@cspell/dict-data-science': 2.0.6 - '@cspell/dict-r@2.0.4': {} + '@cspell/dict-r@2.1.0': {} '@cspell/dict-ruby@5.0.7': {} - '@cspell/dict-rust@4.0.10': {} + '@cspell/dict-rust@4.0.11': {} - '@cspell/dict-scala@5.0.6': {} + '@cspell/dict-scala@5.0.7': {} - '@cspell/dict-software-terms@4.1.20': {} + '@cspell/dict-shell@1.1.0': {} - '@cspell/dict-sql@2.1.8': {} + '@cspell/dict-software-terms@4.2.2': {} - '@cspell/dict-svelte@1.0.5': {} + '@cspell/dict-sql@2.2.0': {} - '@cspell/dict-swift@2.0.4': {} + '@cspell/dict-svelte@1.0.6': {} - '@cspell/dict-terraform@1.0.6': {} + '@cspell/dict-swift@2.0.5': {} - '@cspell/dict-typescript@3.1.11': {} + '@cspell/dict-terraform@1.1.0': {} - '@cspell/dict-vue@3.0.3': {} + '@cspell/dict-typescript@3.2.0': {} - '@cspell/dynamic-import@8.17.1': + '@cspell/dict-vue@3.0.4': {} + + '@cspell/dynamic-import@8.17.2': dependencies: - '@cspell/url': 8.17.1 + '@cspell/url': 8.17.2 import-meta-resolve: 4.1.0 - '@cspell/filetypes@8.17.1': {} + '@cspell/filetypes@8.17.2': {} - '@cspell/strong-weak-map@8.17.1': {} + '@cspell/strong-weak-map@8.17.2': {} - '@cspell/url@8.17.1': {} + '@cspell/url@8.17.2': {} '@es-joy/jsdoccomment@0.49.0': dependencies: @@ -5159,6 +5203,11 @@ snapshots: '@pnpm/network.ca-file': 1.0.2 config-chain: 1.1.13 + '@prettier/sync@0.5.2(prettier@3.4.2)': + dependencies: + make-synchronized: 0.2.9 + prettier: 3.4.2 + '@release-it/conventional-changelog@9.0.3(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.0.0)(release-it@17.10.0(typescript@5.7.2))': dependencies: concat-stream: 2.0.0 @@ -5272,6 +5321,8 @@ snapshots: '@types/katex@0.16.7': {} + '@types/lodash@4.17.14': {} + '@types/mdast@3.0.10': dependencies: '@types/unist': 2.0.6 @@ -5742,6 +5793,8 @@ snapshots: commander@12.1.0: {} + commander@13.0.0: {} + commander@4.1.1: {} commander@8.3.0: {} @@ -5908,61 +5961,61 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 - cspell-config-lib@8.17.1: + cspell-config-lib@8.17.2: dependencies: - '@cspell/cspell-types': 8.17.1 + '@cspell/cspell-types': 8.17.2 comment-json: 4.2.5 - yaml: 2.6.1 + yaml: 2.7.0 - cspell-dictionary@8.17.1: + cspell-dictionary@8.17.2: dependencies: - '@cspell/cspell-pipe': 8.17.1 - '@cspell/cspell-types': 8.17.1 - cspell-trie-lib: 8.17.1 - fast-equals: 5.0.1 + '@cspell/cspell-pipe': 8.17.2 + '@cspell/cspell-types': 8.17.2 + cspell-trie-lib: 8.17.2 + fast-equals: 5.2.2 - cspell-gitignore@8.17.1: + cspell-gitignore@8.17.2: dependencies: - '@cspell/url': 8.17.1 - cspell-glob: 8.17.1 - cspell-io: 8.17.1 + '@cspell/url': 8.17.2 + cspell-glob: 8.17.2 + cspell-io: 8.17.2 find-up-simple: 1.0.0 - cspell-glob@8.17.1: + cspell-glob@8.17.2: dependencies: - '@cspell/url': 8.17.1 + '@cspell/url': 8.17.2 micromatch: 4.0.8 - cspell-grammar@8.17.1: + cspell-grammar@8.17.2: dependencies: - '@cspell/cspell-pipe': 8.17.1 - '@cspell/cspell-types': 8.17.1 + '@cspell/cspell-pipe': 8.17.2 + '@cspell/cspell-types': 8.17.2 - cspell-io@8.17.1: + cspell-io@8.17.2: dependencies: - '@cspell/cspell-service-bus': 8.17.1 - '@cspell/url': 8.17.1 + '@cspell/cspell-service-bus': 8.17.2 + '@cspell/url': 8.17.2 - cspell-lib@8.17.1: + cspell-lib@8.17.2: dependencies: - '@cspell/cspell-bundled-dicts': 8.17.1 - '@cspell/cspell-pipe': 8.17.1 - '@cspell/cspell-resolver': 8.17.1 - '@cspell/cspell-types': 8.17.1 - '@cspell/dynamic-import': 8.17.1 - '@cspell/filetypes': 8.17.1 - '@cspell/strong-weak-map': 8.17.1 - '@cspell/url': 8.17.1 + '@cspell/cspell-bundled-dicts': 8.17.2 + '@cspell/cspell-pipe': 8.17.2 + '@cspell/cspell-resolver': 8.17.2 + '@cspell/cspell-types': 8.17.2 + '@cspell/dynamic-import': 8.17.2 + '@cspell/filetypes': 8.17.2 + '@cspell/strong-weak-map': 8.17.2 + '@cspell/url': 8.17.2 clear-module: 4.1.2 comment-json: 4.2.5 - cspell-config-lib: 8.17.1 - cspell-dictionary: 8.17.1 - cspell-glob: 8.17.1 - cspell-grammar: 8.17.1 - cspell-io: 8.17.1 - cspell-trie-lib: 8.17.1 + cspell-config-lib: 8.17.2 + cspell-dictionary: 8.17.2 + cspell-glob: 8.17.2 + cspell-grammar: 8.17.2 + cspell-io: 8.17.2 + cspell-trie-lib: 8.17.2 env-paths: 3.0.0 - fast-equals: 5.0.1 + fast-equals: 5.2.2 gensequence: 7.0.0 import-fresh: 3.3.0 resolve-from: 5.0.0 @@ -5972,29 +6025,29 @@ snapshots: cspell-populate-words@0.3.0: dependencies: - cspell: 8.17.1 + cspell: 8.17.2 - cspell-trie-lib@8.17.1: + cspell-trie-lib@8.17.2: dependencies: - '@cspell/cspell-pipe': 8.17.1 - '@cspell/cspell-types': 8.17.1 + '@cspell/cspell-pipe': 8.17.2 + '@cspell/cspell-types': 8.17.2 gensequence: 7.0.0 - cspell@8.17.1: + cspell@8.17.2: dependencies: - '@cspell/cspell-json-reporter': 8.17.1 - '@cspell/cspell-pipe': 8.17.1 - '@cspell/cspell-types': 8.17.1 - '@cspell/dynamic-import': 8.17.1 - '@cspell/url': 8.17.1 + '@cspell/cspell-json-reporter': 8.17.2 + '@cspell/cspell-pipe': 8.17.2 + '@cspell/cspell-types': 8.17.2 + '@cspell/dynamic-import': 8.17.2 + '@cspell/url': 8.17.2 chalk: 5.4.1 chalk-template: 1.1.0 - commander: 12.1.0 - cspell-dictionary: 8.17.1 - cspell-gitignore: 8.17.1 - cspell-glob: 8.17.1 - cspell-io: 8.17.1 - cspell-lib: 8.17.1 + commander: 13.0.0 + cspell-dictionary: 8.17.2 + cspell-gitignore: 8.17.2 + cspell-glob: 8.17.2 + cspell-io: 8.17.2 + cspell-lib: 8.17.2 fast-json-stable-stringify: 2.1.0 file-entry-cache: 9.1.0 get-stdin: 9.0.0 @@ -6501,7 +6554,7 @@ snapshots: fast-deep-equal@3.1.3: {} - fast-equals@5.0.1: {} + fast-equals@5.2.2: {} fast-glob@3.3.2: dependencies: @@ -7227,6 +7280,8 @@ snapshots: dependencies: semver: 7.6.3 + make-synchronized@0.2.9: {} + markdown-it@14.1.0: dependencies: argparse: 2.0.1 @@ -7836,14 +7891,14 @@ snapshots: pirates@4.0.6: {} - postcss-load-config@6.0.1(jiti@2.4.0)(postcss@8.4.36)(tsx@4.19.2)(yaml@2.6.1): + postcss-load-config@6.0.1(jiti@2.4.0)(postcss@8.4.36)(tsx@4.19.2)(yaml@2.7.0): dependencies: lilconfig: 3.1.3 optionalDependencies: jiti: 2.4.0 postcss: 8.4.36 tsx: 4.19.2 - yaml: 2.6.1 + yaml: 2.7.0 postcss@8.4.36: dependencies: @@ -8361,7 +8416,7 @@ snapshots: tslib@2.6.2: {} - tsup@8.3.5(jiti@2.4.0)(postcss@8.4.36)(tsx@4.19.2)(typescript@5.7.2)(yaml@2.6.1): + tsup@8.3.5(jiti@2.4.0)(postcss@8.4.36)(tsx@4.19.2)(typescript@5.7.2)(yaml@2.7.0): dependencies: bundle-require: 5.0.0(esbuild@0.24.0) cac: 6.7.14 @@ -8371,7 +8426,7 @@ snapshots: esbuild: 0.24.0 joycon: 3.1.1 picocolors: 1.1.1 - postcss-load-config: 6.0.1(jiti@2.4.0)(postcss@8.4.36)(tsx@4.19.2)(yaml@2.6.1) + postcss-load-config: 6.0.1(jiti@2.4.0)(postcss@8.4.36)(tsx@4.19.2)(yaml@2.7.0) resolve-from: 5.0.0 rollup: 4.28.0 source-map: 0.8.0-beta.0 @@ -8625,6 +8680,8 @@ snapshots: yaml@2.6.1: {} + yaml@2.7.0: {} + yargs-parser@18.1.3: dependencies: camelcase: 5.3.1 diff --git a/src/base.ts b/src/base.ts index f94d5ae43..75626c53d 100644 --- a/src/base.ts +++ b/src/base.ts @@ -22,6 +22,16 @@ import { AllContributorsData } from "./types.js"; import { swallowError } from "./utils/swallowError.js"; import { tryCatchLazyValueAsync } from "./utils/tryCatchLazyValueAsync.js"; +const zContributor = z.object({ + avatar_url: z.string(), + contributions: z.array(z.string()), + login: z.string(), + name: z.string(), + profile: z.string(), +}); + +export type Contributor = z.infer; + export const base = createBase({ options: { access: z @@ -36,15 +46,7 @@ export const base = createBase({ .optional() .describe('value to set in `package.json`\'s `"bin"` property'), contributors: z - .array( - z.object({ - avatar_url: z.string(), - contributions: z.array(z.string()), - login: z.string(), - name: z.string(), - profile: z.string(), - }), - ) + .array(zContributor) .optional() .describe("AllContributors contributors to store in .all-contributorsrc"), description: z @@ -70,6 +72,10 @@ export const base = createBase({ .describe( "email address to be listed as the point of contact in docs and packages", ), + explainer: z + .array(z.string()) + .optional() + .describe("additional README.md sentence(s) describing the package"), funding: z .string() .optional() diff --git a/src/blocks/blockAreTheTypesWrong.ts b/src/blocks/blockAreTheTypesWrong.ts index dfe35c49e..fe16041f1 100644 --- a/src/blocks/blockAreTheTypesWrong.ts +++ b/src/blocks/blockAreTheTypesWrong.ts @@ -13,6 +13,7 @@ export const blockAreTheTypesWrong = base.createBlock({ { name: "Are The Types Wrong?", steps: [ + { run: "pnpm build" }, { run: "npx --yes @arethetypeswrong/cli --pack . --ignore-rules cjs-resolves-to-esm", }, diff --git a/src/blocks/blockCodecov.ts b/src/blocks/blockCodecov.ts new file mode 100644 index 000000000..961eaf19d --- /dev/null +++ b/src/blocks/blockCodecov.ts @@ -0,0 +1,21 @@ +import { base } from "../base.js"; +import { blockVitest } from "./blockVitest.js"; + +export const blockCodecov = base.createBlock({ + about: { + name: "Codecov", + }, + produce() { + return { + addons: [ + blockVitest({ + coverage: { + env: { + CODECOV_TOKEN: "${{ secrets.CODECOV_TOKEN }}", + }, + }, + }), + ], + }; + }, +}); diff --git a/src/blocks/blockDevelopmentDocs.test.ts b/src/blocks/blockDevelopmentDocs.test.ts index 8b278e08b..e07c4c126 100644 --- a/src/blocks/blockDevelopmentDocs.test.ts +++ b/src/blocks/blockDevelopmentDocs.test.ts @@ -19,7 +19,7 @@ describe("blockDevelopmentDocs", () => { After [forking the repo from GitHub](https://help.github.com/articles/fork-a-repo) and [installing pnpm](https://pnpm.io/installation): \`\`\`shell - git clone https://github.com//test-repository + git clone https://github.com/(your-name-here)/test-repository cd test-repository pnpm install \`\`\` @@ -45,7 +45,7 @@ describe("blockDevelopmentDocs", () => { After [forking the repo from GitHub](https://help.github.com/articles/fork-a-repo) and [installing pnpm](https://pnpm.io/installation): \`\`\`shell - git clone https://github.com//test-repository + git clone https://github.com/(your-name-here)/test-repository cd test-repository pnpm install \`\`\` @@ -110,7 +110,7 @@ describe("blockDevelopmentDocs", () => { After [forking the repo from GitHub](https://help.github.com/articles/fork-a-repo) and [installing pnpm](https://pnpm.io/installation): \`\`\`shell - git clone https://github.com//test-repository + git clone https://github.com/(your-name-here)/test-repository cd test-repository pnpm install \`\`\` @@ -166,7 +166,7 @@ describe("blockDevelopmentDocs", () => { After [forking the repo from GitHub](https://help.github.com/articles/fork-a-repo) and [installing pnpm](https://pnpm.io/installation): \`\`\`shell - git clone https://github.com//test-repository + git clone https://github.com/(your-name-here)/test-repository cd test-repository pnpm install \`\`\` diff --git a/src/blocks/blockDevelopmentDocs.ts b/src/blocks/blockDevelopmentDocs.ts index 65c85ecde..fa3e40756 100644 --- a/src/blocks/blockDevelopmentDocs.ts +++ b/src/blocks/blockDevelopmentDocs.ts @@ -88,7 +88,7 @@ export const blockDevelopmentDocs = base.createBlock({ `After [forking the repo from GitHub](https://help.github.com/articles/fork-a-repo) and [installing pnpm](https://pnpm.io/installation):`, ``, `\`\`\`shell`, - `git clone https://github.com//${options.repository}`, + `git clone https://github.com/(your-name-here)/${options.repository}`, `cd ${options.repository}`, `pnpm install`, `\`\`\``, diff --git a/src/blocks/blockESLint.ts b/src/blocks/blockESLint.ts index c4280bea6..5dc643119 100644 --- a/src/blocks/blockESLint.ts +++ b/src/blocks/blockESLint.ts @@ -56,7 +56,9 @@ export const blockESLint = base.createBlock({ name: "ESLint", }, addons: { + allowDefaultProject: z.array(z.string()).default([]), beforeLint: z.string().optional(), + explanations: z.array(z.string()).default([]), extensions: z.array(z.union([z.string(), zExtension])).default([]), ignores: z.array(z.string()).default([]), imports: z.array(zPackageImport).default([]), @@ -74,7 +76,14 @@ export const blockESLint = base.createBlock({ }; }, produce({ addons, options }) { - const { extensions, ignores, imports, rules, settings } = addons; + const { + allowDefaultProject, + extensions, + ignores, + imports, + rules, + settings, + } = addons; const importLines = [ 'import eslint from "@eslint/js";', @@ -101,7 +110,13 @@ export const blockESLint = base.createBlock({ languageOptions: { parserOptions: { projectService: { - allowDefaultProject: ["*.config.*s"], + allowDefaultProject: Array.from( + new Set( + ["*.config.*s", ...allowDefaultProject, options.bin] + .filter(Boolean) + .sort(), + ), + ), }, tsconfigRootDir: "import.meta.dirname", }, @@ -197,7 +212,7 @@ Each should be shown in VS Code, and can be run manually on the command-line: }), ], files: { - "eslint.config.js": `${importLines.join("\n")} + "eslint.config.js": `${addons.explanations.map((explanation) => `/*\n${explanation}\n*/\n\n`).join("\n")}${importLines.join("\n")} export default tseslint.config( { ignores: [${ignoreLines.join(", ")}] }, diff --git a/src/blocks/blockESLintNode.ts b/src/blocks/blockESLintNode.ts index 19762c5b9..bcc241244 100644 --- a/src/blocks/blockESLintNode.ts +++ b/src/blocks/blockESLintNode.ts @@ -14,12 +14,14 @@ export const blockESLintNode = base.createBlock({ { extends: ["tseslint.configs.disableTypeChecked"], files: ["**/*.md/*.ts"], - rules: { - "n/no-missing-import": [ - "error", - { allowModules: [options.repository] }, - ], - }, + rules: options.usage?.includes(`from "${options.repository}`) + ? { + "n/no-missing-import": [ + "error", + { allowModules: [options.repository] }, + ], + } + : undefined, }, ], imports: [{ source: "eslint-plugin-n", specifier: "n" }], diff --git a/src/blocks/blockKnip.ts b/src/blocks/blockKnip.ts index 1883a6431..411e1708e 100644 --- a/src/blocks/blockKnip.ts +++ b/src/blocks/blockKnip.ts @@ -1,3 +1,5 @@ +import { z } from "zod"; + import { base } from "../base.js"; import { getPackageDependencies, @@ -12,6 +14,9 @@ export const blockKnip = base.createBlock({ about: { name: "Knip", }, + addons: { + ignoreDependencies: z.array(z.string()).optional(), + }, migrate() { return { scripts: [ @@ -22,7 +27,8 @@ export const blockKnip = base.createBlock({ ], }; }, - produce() { + produce({ addons }) { + const { ignoreDependencies } = addons; return { addons: [ blockDevelopmentDocs({ @@ -56,12 +62,13 @@ export const blockKnip = base.createBlock({ files: { "knip.json": JSON.stringify({ $schema: `https://unpkg.com/knip@${getPackageDependency("knip")}/schema.json`, - entry: ["src/index.ts!"], + entry: ["src/index.ts", "src/**/*.test.*"], + ignoreDependencies, ignoreExportsUsedInFile: { interface: true, type: true, }, - project: ["src/**/*.ts!"], + project: ["src/**/*.ts"], }), }, }; diff --git a/src/blocks/blockMarkdownlint.ts b/src/blocks/blockMarkdownlint.ts index 4410f601c..314cad795 100644 --- a/src/blocks/blockMarkdownlint.ts +++ b/src/blocks/blockMarkdownlint.ts @@ -7,6 +7,7 @@ import { blockDevelopmentDocs } from "./blockDevelopmentDocs.js"; import { blockGitHubActionsCI } from "./blockGitHubActionsCI.js"; import { blockPackageJson } from "./blockPackageJson.js"; import { blockVSCode } from "./blockVSCode.js"; +import { formatIgnoreFile } from "./files/formatIgnoreFile.js"; import { CommandPhase } from "./phases.js"; export const blockMarkdownlint = base.createBlock({ @@ -66,14 +67,14 @@ export const blockMarkdownlint = base.createBlock({ "first-line-h1": false, "no-inline-html": false, }), - ".markdownlintignore": [ - ".github/CODE_OF_CONDUCT.md", - "CHANGELOG.md", - "node_modules/", - ...ignores, - ] - .sort() - .join("\n"), + ".markdownlintignore": formatIgnoreFile( + [ + ".github/CODE_OF_CONDUCT.md", + "CHANGELOG.md", + "node_modules/", + ...ignores, + ].sort(), + ), }, scripts: [ { diff --git a/src/blocks/blockPackageJson.ts b/src/blocks/blockPackageJson.ts index 5995c2017..50a677fac 100644 --- a/src/blocks/blockPackageJson.ts +++ b/src/blocks/blockPackageJson.ts @@ -45,7 +45,9 @@ export const blockPackageJson = base.createBlock({ ...options.packageData?.devDependencies, ...addons.properties.devDependencies, }; - const description = htmlToText.convert(options.description); + const description = htmlToText.convert(options.description, { + wordwrap: false, + }); return { files: { diff --git a/src/blocks/blockPrettier.ts b/src/blocks/blockPrettier.ts index f08f1c4fb..ce2d7147a 100644 --- a/src/blocks/blockPrettier.ts +++ b/src/blocks/blockPrettier.ts @@ -7,6 +7,7 @@ import { blockDevelopmentDocs } from "./blockDevelopmentDocs.js"; import { blockGitHubActionsCI } from "./blockGitHubActionsCI.js"; import { blockPackageJson } from "./blockPackageJson.js"; import { blockVSCode } from "./blockVSCode.js"; +import { formatIgnoreFile } from "./files/formatIgnoreFile.js"; import { CommandPhase } from "./phases.js"; export const blockPrettier = base.createBlock({ @@ -94,11 +95,11 @@ pnpm format --write files: { ".husky": { ".gitignore": "_\n", - "pre-commit": ["npx lint-staged\n", { mode: 0x777 }], + "pre-commit": ["npx lint-staged\n", { mode: 33279 }], }, - ".prettierignore": ["/.husky", "/lib", "/pnpm-lock.yaml", ...ignores] - .sort() - .join("\n"), + ".prettierignore": formatIgnoreFile( + ["/.husky", "/lib", "/pnpm-lock.yaml", ...ignores].sort(), + ), ".prettierrc.json": JSON.stringify({ $schema: "http://json.schemastore.org/prettierrc", ...(overrides.length && { overrides: overrides.sort() }), diff --git a/src/blocks/blockREADME.ts b/src/blocks/blockREADME.ts index 604894ea8..52027a1f7 100644 --- a/src/blocks/blockREADME.ts +++ b/src/blocks/blockREADME.ts @@ -1,10 +1,12 @@ +import _ from "lodash"; import { z } from "zod"; -import { base } from "../base.js"; +import { base, Contributor } from "../base.js"; function printAttributes(attributes: Record) { return Object.entries(attributes) .map(([key, value]) => `${key}="${value}"`) + .sort() .join(" "); } @@ -17,10 +19,13 @@ export const blockREADME = base.createBlock({ }, produce({ addons, options }) { const { notices } = addons; + const { contributors = [] } = options; - const logo = options.logo - ? `\n\n` - : ""; + const logo = + options.logo && + `\n`; + + const explainer = options.explainer && `\n${options.explainer.join("\n")}`; return { files: { @@ -31,6 +36,7 @@ export const blockREADME = base.createBlock({

+ 👪 All Contributors: ${contributors.length} 🤝 Code of Conduct: Kept @@ -39,7 +45,7 @@ export const blockREADME = base.createBlock({ 📦 npm version 💪 TypeScript: Strict

-${logo} +${[logo, explainer].filter(Boolean).join("\n\n")} ## Usage ${options.usage} @@ -52,8 +58,7 @@ Thanks! 💖 ## Contributors - - +${printAllContributorsTable(contributors)} ${notices.length ? `\n${notices.map((notice) => notice.trim()).join("\n\n")}` : ""}`, }, @@ -66,5 +71,70 @@ function formatDescription(description: string) { return description; } - return "\n\t" + description.replaceAll(". ", ". \n\t") + "\n"; + return "\n\t" + description.replaceAll(". ", ".\n\t") + "\n"; +} + +function printAllContributorsTable(contributors: Contributor[]) { + return [ + ``, + ``, + ``, + ``, + ` `, + ` `, + // This intentionally uses the same sort as all-contributors-cli: + // https://github.com/all-contributors/cli/blob/74bc388bd6f0ae2658e6495e9d3781d737438a97/src/generate/index.js#L76 + ..._.sortBy(contributors, "name").flatMap((contributor, i) => { + const row = printContributorCell(contributor); + + return i && i % 7 === 0 ? [` `, ` `, row] : [row]; + }), + ` `, + ` `, + `
`, + ``, + ``, + ``, + ``, + ``, + ].join("\n"); +} + +function printContributorCell(contributor: Contributor) { + return [ + ` `, + ``, + `${contributor.name}`, + `
`, + `${contributor.name}

`, + ...contributor.contributions + .map((contribution) => { + switch (contribution) { + case "bug": + return `🐛`; + case "code": + return `💻`; + case "design": + return `🎨`; + case "doc": + return `📖`; + case "ideas": + return `🤔`; + case "infra": + return `🚇`; + case "maintenance": + return `🚧`; + case "review": + return `👀`; + case "test": + return `⚠️`; + case "tool": + return `🔧`; + default: + return `!(${contribution})!`; + } + }) + .join(" "), + ``, + ].join(""); } diff --git a/src/blocks/blockTSup.ts b/src/blocks/blockTSup.ts index 56efc77a2..129db89d9 100644 --- a/src/blocks/blockTSup.ts +++ b/src/blocks/blockTSup.ts @@ -14,6 +14,7 @@ export const blockTSup = base.createBlock({ }, addons: { entry: z.array(z.string()).default([]), + runArgs: z.array(z.string()).default([]), }, migrate() { return { @@ -56,7 +57,12 @@ pnpm build --watch jobs: [ { name: "Build", - steps: [{ run: "pnpm build" }, { run: "node ./lib/index.js" }], + steps: [ + { run: "pnpm build" }, + { + run: `node ./lib/index.js${addons.runArgs.map((arg) => ` ${arg}`).join("")}`, + }, + ], }, ], }), diff --git a/src/blocks/blockVitest.test.ts b/src/blocks/blockVitest.test.ts index 7e36afecb..8833d77be 100644 --- a/src/blocks/blockVitest.test.ts +++ b/src/blocks/blockVitest.test.ts @@ -512,7 +512,6 @@ describe("blockVitest", () => { const creation = testBlock(blockVitest, { addons: { coverage: { - directory: "coverage*", exclude: ["other"], flags: "unit", include: ["src/"], @@ -528,7 +527,7 @@ describe("blockVitest", () => { { "addons": { "ignores": [ - "coverage*", + "coverage", ], }, "block": [Function], @@ -581,7 +580,7 @@ describe("blockVitest", () => { }, ], "ignores": [ - "coverage*", + "coverage", "**/*.snap", ], "imports": [ @@ -648,7 +647,7 @@ describe("blockVitest", () => { { "addons": { "ignores": [ - "/coverage*", + "/coverage", ], }, "block": [Function], @@ -705,7 +704,7 @@ describe("blockVitest", () => { { "addons": { "ignores": [ - "/coverage*", + "/coverage", ], }, "block": [Function], diff --git a/src/blocks/blockVitest.ts b/src/blocks/blockVitest.ts index ee03c306c..ccbda437e 100644 --- a/src/blocks/blockVitest.ts +++ b/src/blocks/blockVitest.ts @@ -22,13 +22,12 @@ export const blockVitest = base.createBlock({ addons: { coverage: z .object({ - directory: z.string().optional(), + env: z.record(z.string(), z.string()).optional(), exclude: z.array(z.string()).optional(), flags: z.string().optional(), include: z.array(z.string()).optional(), }) .default({}), - env: z.record(z.string(), z.string()).default({}), exclude: z.array(z.string()).default([]), flags: z.array(z.string()).default([]), }, @@ -45,14 +44,14 @@ export const blockVitest = base.createBlock({ }; }, produce({ addons }) { - const { coverage, env, exclude = [], flags } = addons; - const coverageDirectory = coverage.directory ?? "coverage"; + const { coverage, exclude = [], flags } = addons; + const { env = {} } = coverage; const excludeText = JSON.stringify(exclude); return { addons: [ blockCSpell({ - ignores: [coverageDirectory], + ignores: ["coverage"], }), blockDevelopmentDocs({ sections: { @@ -93,7 +92,7 @@ Calls to \`console.log\`, \`console.warn\`, and other console methods will cause ], }, ], - ignores: [coverageDirectory, "**/*.snap"], + ignores: ["coverage", "**/*.snap"], imports: [{ source: "@vitest/eslint-plugin", specifier: "vitest" }], }), blockExampleFiles({ @@ -146,7 +145,7 @@ describe("greet", () => { }, }), blockGitignore({ - ignores: [`/${coverageDirectory}`], + ignores: ["/coverage"], }), blockGitHubActionsCI({ jobs: [ @@ -186,7 +185,7 @@ describe("greet", () => { }, }), blockPrettier({ - ignores: [`/${coverageDirectory}`], + ignores: ["/coverage"], }), blockTSup({ entry: ["!src/**/*.test.*"], diff --git a/src/blocks/files/createMultiWorkflowFile.ts b/src/blocks/files/createMultiWorkflowFile.ts index 0d38befe6..84dc8130f 100644 --- a/src/blocks/files/createMultiWorkflowFile.ts +++ b/src/blocks/files/createMultiWorkflowFile.ts @@ -22,7 +22,7 @@ export function createMultiWorkflowFile({ return formatWorkflowYaml({ jobs: Object.fromEntries( jobs.map((job) => [ - job.name.toLowerCase().replaceAll(" ", "_"), + job.name.toLowerCase().replaceAll(" ", "_").replaceAll("?", ""), { name: job.name, "runs-on": "ubuntu-latest", diff --git a/src/blocks/files/createSoloWorkflowFile.ts b/src/blocks/files/createSoloWorkflowFile.ts index 6049a8bb9..1847103fa 100644 --- a/src/blocks/files/createSoloWorkflowFile.ts +++ b/src/blocks/files/createSoloWorkflowFile.ts @@ -75,7 +75,7 @@ export function createSoloWorkflowFile({ return formatWorkflowYaml({ concurrency, jobs: { - [name.replaceAll(" ", "_").toLowerCase()]: { + [name.replaceAll(" ", "_").toLowerCase().replaceAll("?", "")]: { ...(options.if && { if: options.if }), "runs-on": "ubuntu-latest", steps: diff --git a/src/docs.test.ts b/src/docs.test.ts index 19fabd8fd..6134303c1 100644 --- a/src/docs.test.ts +++ b/src/docs.test.ts @@ -8,13 +8,29 @@ import { blocks, presets } from "./index.js"; const actualLines = await createActualLines(); const expectedLines = await createExpectedLines(); -describe("Blocks.md", () => { +// This test ensures ensures docs/Blocks.md has a row for each of CTA's options. +// Each row should include emojis describing which preset(s) include the option. +// +// If this fails, it's likely due to adding, removing, or renaming a block. +// You may need to manually change docs/Blocks.md to match to those changes. +// +// For example, if you add a blockExample to the Common and Everything presets, +// you'll need to add a row like: +// +// ```md +// | Example | `--exclude-example` | | ✅ | 💯 | +// ``` +// +// Rows are kept sorted by alphabetical order of name. +describe("docs/Blocks.md", () => { for (const [i, line] of expectedLines.entries()) { const name = line.split(" | ")[0].replace("| ", "").trim(); if (!name) { continue; } + // TODO: Enable vitest/eslint-plugin type-checking: + // https://github.com/vitest-dev/eslint-plugin-vitest?tab=readme-ov-file#enabling-with-type-testing // eslint-disable-next-line vitest/valid-title test(name, () => { const actualLine = actualLines.find((line) => line.includes(`| ${name}`)); diff --git a/src/index.test.ts b/src/index.test.ts deleted file mode 100644 index 01fc1a2f8..000000000 --- a/src/index.test.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { produceBase, producePreset } from "create"; -import { describe, test } from "vitest"; - -import { base, BaseOptions, presets } from "./index.js"; - -describe("index", () => { - test("end-to-end", async () => { - const created = await producePreset(presets.everything, { - options: (await produceBase(base)) as BaseOptions, - }); - - const actual = await readFilesForCreate("."); - - console.log(created); - }); -}); diff --git a/src/integration.test.ts b/src/integration.test.ts new file mode 100644 index 000000000..fd2cee794 --- /dev/null +++ b/src/integration.test.ts @@ -0,0 +1,128 @@ +import prettier from "@prettier/sync"; +import { produceBase, producePreset } from "create"; +import { intakeFromDirectory } from "create-fs"; +import { diffCreatedDirectory } from "create-testers"; +import { expect, test } from "vitest"; + +import { + base, + BaseOptions, + blockAreTheTypesWrong, + blockCSpell, + blockESLint, + blockKnip, + blockTemplatedWith, + blockTSup, + presets, +} from "./index.js"; + +// This test checks the `create` production using options inferred from disk, +// along with some explicit addons and blocks specified. +// It ensures that result has no differences from the actual files on disk. +// +// If the test fails, it's most likely due to a block being changed without the +// corresponding file(s) on disk also being changed. +// You may need to manually update files on disk to match the block's output. +// +// The next most likely culprit for failures is changing file contents that are +// specified by the addons mentioned in the producePreset() call below. +// For now, if you change the output on disk, you'll need to manually update here too. +// TODO: Eventually the create engine will be able to infer them: +// https://github.com/JoshuaKGoldberg/create/issues/128 +// +// For example, if you change blockTypeScript's target from "ES2022 to "ES2023", +// you'll also need to update the ./tsconfig.json on disk in the same way. +test("Producing the everything preset matches the files in this repository", async () => { + const actual = await intakeFromDirectory(".", { + exclude: /node_modules|^\.git$/, + }); + + const created = await producePreset(presets.everything, { + addons: [ + blockCSpell({ + words: [ + "Anson", + "apexskier", + "dbaeumer", + "joshuakgoldberg", + "markdownlintignore", + "mtfoley", + "infile", + "npmjs", + ], + }), + blockESLint({ + explanations: [ + `👋 Hi! This ESLint configuration contains a lot more stuff than many repos'! +You can read from it to see all sorts of linting goodness, but don't worry - +it's not something you need to exhaustively understand immediately. 💙 + +If you're interested in learning more, see the 'getting started' docs on: +- ESLint: https://eslint.org +- typescript-eslint: https://typescript-eslint.io`, + ], + rules: [ + { + comment: + "These on-by-default rules work well for this repo if configured", + entries: { + "@typescript-eslint/prefer-nullish-coalescing": [ + "error", + { ignorePrimitives: true }, + ], + "@typescript-eslint/restrict-template-expressions": [ + "error", + { allowBoolean: true, allowNullish: true, allowNumber: true }, + ], + }, + }, + ], + }), + blockKnip({ + ignoreDependencies: ["all-contributors-cli", "cspell-populate-words"], + }), + blockTSup({ + runArgs: ["--version"], + }), + ], + blocks: { + add: [blockAreTheTypesWrong], + exclude: [blockTemplatedWith], + }, + options: (await produceBase(base)) as BaseOptions, + }); + + const processText = (text: string, filePath: string) => + /all-contributorsrc|js|md|ts|yml/.test(filePath) + ? prettier.format(text, { filepath: filePath, useTabs: true }) + : text; + + // Right now, there is exactly one change: the altered beta release flow + // TODO: That will be removed once releases switch back to stable: + // https://github.com/JoshuaKGoldberg/create-typescript-app/issues/1831 + expect(diffCreatedDirectory(actual, created.files, processText)) + .toMatchInlineSnapshot(` + { + ".github": { + "workflows": { + "release.yml": "@@ -13,9 +13,13 @@ + - run: pnpm build + - env: + GITHUB_TOKEN: \${{ secrets.ACCESS_TOKEN }} + NPM_TOKEN: \${{ secrets.NPM_TOKEN }} + - uses: JoshuaKGoldberg/release-it-action@v0.2.2 + + run: | + + git config --global user.email "git@joshuakgoldberg.com" + + git config --global user.name "Josh Goldberg" + + npm config set //registry.npmjs.org/:_authToken $NPM_TOKEN + + - run: npx release-it --preRelease=beta + + name: Release + + on: + ", + }, + }, + } + `); +}); diff --git a/src/options/readDefaultsFromReadme.ts b/src/options/readDefaultsFromReadme.ts index f27492b21..2204ff962 100644 --- a/src/options/readDefaultsFromReadme.ts +++ b/src/options/readDefaultsFromReadme.ts @@ -13,6 +13,11 @@ export function readDefaultsFromReadme( ); return { + explainer: () => [ + `\`create-typescript-app\` is a one-stop-shop solution to set up a new or existing repository with the latest and greatest TypeScript tooling.`, + `It includes options not just for building and testing but also GitHub repository templates, contributor recognition, automated release management, and more.`, + ], + logo: async () => { const tag = await imageTag(); @@ -37,6 +42,7 @@ export function readDefaultsFromReadme( ...readLogoSizing(src), }; }, + title: async () => { const text = await readme(); const fromText = (/^(.+)<\/h1>/.exec(text) ?? diff --git a/src/options/readLogoSizing.ts b/src/options/readLogoSizing.ts index 6c7857780..b8dad3b26 100644 --- a/src/options/readLogoSizing.ts +++ b/src/options/readLogoSizing.ts @@ -24,7 +24,7 @@ export function readLogoSizing( } if (size.height <= maximum && size.width <= maximum) { - return size; + return { height: size.height, width: size.height }; } return size.height > size.width diff --git a/src/presets/common.ts b/src/presets/common.ts index b33f738ac..8211e4304 100644 --- a/src/presets/common.ts +++ b/src/presets/common.ts @@ -1,5 +1,6 @@ import { base } from "../base.js"; import { blockAllContributors } from "../blocks/blockAllContributors.js"; +import { blockCodecov } from "../blocks/blockCodecov.js"; import { blockReleaseIt } from "../blocks/blockReleaseIt.js"; import { blockVitest } from "../blocks/blockVitest.js"; import { presetMinimal } from "./minimal.js"; @@ -13,6 +14,7 @@ export const presetCommon = base.createPreset({ blocks: [ ...presetMinimal.blocks, blockAllContributors, + blockCodecov, blockReleaseIt, blockVitest, ], From b62862a85c0db35c9a3cd3af8a2dcf1658c0ac61 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Tue, 14 Jan 2025 13:46:06 -0500 Subject: [PATCH 03/22] alpha.13 --- package.json | 5 +- pnpm-lock.yaml | 359 ++++++++++++++++++--- src/blocks/blockRepositoryBranchRuleset.ts | 1 - 3 files changed, 314 insertions(+), 51 deletions(-) diff --git a/package.json b/package.json index ca74bf951..5f6a4a313 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,8 @@ }, "dependencies": { "chalk": "^5.4.1", - "create": "0.1.0-alpha.11", + "create": "0.1.0-alpha.13", + "create-fs": "^0.1.0", "cspell-populate-words": "^0.3.0", "execa": "^9.5.2", "git-remote-origin-url": "^4.0.0", @@ -76,7 +77,7 @@ "@vitest/eslint-plugin": "1.1.20", "all-contributors-cli": "6.26.1", "console-fail-test": "0.5.0", - "create-testers": "0.1.0-alpha.11", + "create-testers": "0.1.0-alpha.13", "cspell": "^8.17.2", "eslint": "9.17.0", "eslint-plugin-jsdoc": "50.6.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2bfc4dc7b..c0fca1afb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,8 +12,11 @@ importers: specifier: ^5.4.1 version: 5.4.1 create: - specifier: 0.1.0-alpha.11 - version: 0.1.0-alpha.11(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.0.0)(typescript@5.7.2) + specifier: 0.1.0-alpha.13 + version: 0.1.0-alpha.13 + create-fs: + specifier: ^0.1.0 + version: 0.1.0 cspell-populate-words: specifier: ^0.3.0 version: 0.3.0 @@ -34,13 +37,13 @@ importers: version: 1.2.0 input-from-file: specifier: 0.1.0-alpha.4 - version: 0.1.0-alpha.4(create@0.1.0-alpha.11(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.0.0)(typescript@5.7.2)) + version: 0.1.0-alpha.4(create@0.1.0-alpha.13) input-from-file-json: specifier: 0.1.0-alpha.4 - version: 0.1.0-alpha.4(create@0.1.0-alpha.11(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.0.0)(typescript@5.7.2)) + version: 0.1.0-alpha.4(create@0.1.0-alpha.13) input-from-script: specifier: 0.1.0-alpha.4 - version: 0.1.0-alpha.4(create@0.1.0-alpha.11(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.0.0)(typescript@5.7.2)) + version: 0.1.0-alpha.4(create@0.1.0-alpha.13) js-yaml: specifier: ^4.1.0 version: 4.1.0 @@ -121,8 +124,8 @@ importers: specifier: 0.5.0 version: 0.5.0 create-testers: - specifier: 0.1.0-alpha.11 - version: 0.1.0-alpha.11(create@0.1.0-alpha.11(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.0.0)(typescript@5.7.2)) + specifier: 0.1.0-alpha.13 + version: 0.1.0-alpha.13(create-fs@0.1.0)(create@0.1.0-alpha.13) cspell: specifier: ^8.17.2 version: 8.17.2 @@ -259,11 +262,11 @@ packages: '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - '@clack/core@0.4.0': - resolution: {integrity: sha512-YJCYBsyJfNDaTbvDUVSJ3SgSuPrcujarRgkJ5NLjexDZKvaOiVVJvAQYx8lIgG0qRT8ff0fPgqyBCVivanIZ+A==} + '@clack/core@0.4.1': + resolution: {integrity: sha512-Pxhij4UXg8KSr7rPek6Zowm+5M22rbd2g1nfojHJkxp5YkFqiZ2+YLEM/XGVIzvGOcM0nqjIFxrpDwWRZYWYjA==} - '@clack/prompts@0.9.0': - resolution: {integrity: sha512-nGsytiExgUr4FL0pR/LeqxA28nz3E0cW7eLTSh3Iod9TGrbBt8Y7BHbV3mmkNC4G0evdYyQ3ZsbiBkk7ektArA==} + '@clack/prompts@0.9.1': + resolution: {integrity: sha512-JIpyaboYZeWYlyP0H+OoPPxd6nqueG/CmN6ixBiNFsIDHREevjIf0n0Ohh5gr5C8pEDknzgvz+pIJ8dMhzWIeg==} '@conventional-changelog/git-client@1.0.1': resolution: {integrity: sha512-PJEqBwAleffCMETaVm/fUgHldzBE35JFk3/9LL6NUA5EXa3qednu+UT6M7E5iBu3zIQZCULYIiZ90fBYHt6xUw==} @@ -1039,22 +1042,42 @@ packages: resolution: {integrity: sha512-nwSjC349E6/wruMCo944y1dBC7uKzUYrBMoC4Qx/xfLLBmD+R66oMKB1jXS2HYRF9Hqh/Alq3UNRggVWZxjvUg==} engines: {node: '>= 18'} + '@octokit/app@15.1.2': + resolution: {integrity: sha512-6aKmKvqnJKoVK+kx0mLlBMKmQYoziPw4Rd/PWr0j65QVQlrDXlu6hGU8fmTXt7tNkf/DsubdIaTT4fkoWzCh5g==} + engines: {node: '>= 18'} + '@octokit/auth-app@7.1.0': resolution: {integrity: sha512-cazGaJPSgeZ8NkVYeM/C5l/6IQ5vZnsI8p1aMucadCkt/bndI+q+VqwrlnWbASRmenjOkf1t1RpCKrif53U8gw==} engines: {node: '>= 18'} + '@octokit/auth-app@7.1.4': + resolution: {integrity: sha512-5F+3l/maq9JfWQ4bV28jT2G/K8eu9OJ317yzXPTGe4Kw+lKDhFaS4dQ3Ltmb6xImKxfCQdqDqMXODhc9YLipLw==} + engines: {node: '>= 18'} + '@octokit/auth-oauth-app@8.1.1': resolution: {integrity: sha512-5UtmxXAvU2wfcHIPPDWzVSAWXVJzG3NWsxb7zCFplCWEmMCArSZV0UQu5jw5goLQXbFyOr5onzEH37UJB3zQQg==} engines: {node: '>= 18'} + '@octokit/auth-oauth-app@8.1.2': + resolution: {integrity: sha512-3woNZgq5/S6RS+9ZTq+JdymxVr7E0s4EYxF20ugQvgX3pomdPUL5r/XdTY9wALoBM2eHVy4ettr5fKpatyTyHw==} + engines: {node: '>= 18'} + '@octokit/auth-oauth-device@7.1.1': resolution: {integrity: sha512-HWl8lYueHonuyjrKKIup/1tiy0xcmQCdq5ikvMO1YwkNNkxb6DXfrPjrMYItNLyCP/o2H87WuijuE+SlBTT8eg==} engines: {node: '>= 18'} + '@octokit/auth-oauth-device@7.1.2': + resolution: {integrity: sha512-gTOIzDeV36OhVfxCl69FmvJix7tJIiU6dlxuzLVAzle7fYfO8UDyddr9B+o4CFQVaMBLMGZ9ak2CWMYcGeZnPw==} + engines: {node: '>= 18'} + '@octokit/auth-oauth-user@5.1.1': resolution: {integrity: sha512-rRkMz0ErOppdvEfnemHJXgZ9vTPhBuC6yASeFaB7I2yLMd7QpjfrL1mnvRPlyKo+M6eeLxrKanXJ9Qte29SRsw==} engines: {node: '>= 18'} + '@octokit/auth-oauth-user@5.1.2': + resolution: {integrity: sha512-PgVDDPJgZYb3qSEXK4moksA23tfn68zwSAsQKZ1uH6IV9IaNEYx35OXXI80STQaLYnmEE86AgU0tC1YkM4WjsA==} + engines: {node: '>= 18'} + '@octokit/auth-token@4.0.0': resolution: {integrity: sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==} engines: {node: '>= 18'} @@ -1067,6 +1090,10 @@ packages: resolution: {integrity: sha512-zPSmfrUAcspZH/lOFQnVnvjQZsIvmfApQH6GzJrkIunDooU1Su2qt2FfMTSVPRp7WLTQyC20Kd55lF+mIYaohQ==} engines: {node: '>= 18'} + '@octokit/auth-unauthenticated@6.1.1': + resolution: {integrity: sha512-bGXqdN6RhSFHvpPq46SL8sN+F3odQ6oMNLWc875IgoqcC3qus+fOL2th6Tkl94wvdSTy8/OeHzWy/lZebmnhog==} + engines: {node: '>= 18'} + '@octokit/core@5.2.0': resolution: {integrity: sha512-1LFfa/qnMQvEOAdzlQymH0ulepxbxnCYAKJZfMci/5XJyIHWgEYnDmgnKakbTh7CH2tFQ5O60oYDvns4i9RAIg==} engines: {node: '>= 18'} @@ -1075,6 +1102,10 @@ packages: resolution: {integrity: sha512-hEb7Ma4cGJGEUNOAVmyfdB/3WirWMg5hDuNFVejGEDFqupeOysLc2sG6HJxY2etBp5YQu5Wtxwi020jS9xlUwg==} engines: {node: '>= 18'} + '@octokit/core@6.1.3': + resolution: {integrity: sha512-z+j7DixNnfpdToYsOutStDgeRzJSMnbj8T1C/oQjB6Aa+kRfNjs/Fn7W6c8bmlt6mfy3FkgeKBRnDjxQow5dow==} + engines: {node: '>= 18'} + '@octokit/endpoint@10.1.1': resolution: {integrity: sha512-JYjh5rMOwXMJyUpj028cu0Gbp7qe/ihxfJMLc8VZBMMqSwLgOxDI1911gV4Enl1QSavAQNJcwmwBF9M0VvLh6Q==} engines: {node: '>= 18'} @@ -1091,10 +1122,18 @@ packages: resolution: {integrity: sha512-ukiRmuHTi6ebQx/HFRCXKbDlOh/7xEV6QUXaE7MJEKGNAncGI/STSbOkl12qVXZrfZdpXctx5O9X1AIaebiDBg==} engines: {node: '>= 18'} + '@octokit/graphql@8.1.2': + resolution: {integrity: sha512-bdlj/CJVjpaz06NBpfHhp4kGJaRZfz7AzC+6EwUImRtrwIw8dIgJ63Xg0OzV9pRn3rIzrt5c2sa++BL0JJ8GLw==} + engines: {node: '>= 18'} + '@octokit/oauth-app@7.1.2': resolution: {integrity: sha512-4ntCOZIiTozKwuYQroX/ZD722tzMH8Eicv/cgDM/3F3lyrlwENHDv4flTCBpSJbfK546B2SrkKMWB+/HbS84zQ==} engines: {node: '>= 18'} + '@octokit/oauth-app@7.1.5': + resolution: {integrity: sha512-/Y2MiwWDlGUK4blKKfjJiwjzu/FzwKTTTfTZAAQ0QbdBIDEGJPWhOFH6muSN86zaa4tNheB4YS3oWIR2e4ydzA==} + engines: {node: '>= 18'} + '@octokit/oauth-authorization-url@7.1.1': resolution: {integrity: sha512-ooXV8GBSabSWyhLUowlMIVd9l1s2nsOGQdlP2SQ4LnkEsGXzeCvbSbCPdZThXhEFzleGPwbapT0Sb+YhXRyjCA==} engines: {node: '>= 18'} @@ -1103,24 +1142,46 @@ packages: resolution: {integrity: sha512-C5lglRD+sBlbrhCUTxgJAFjWgJlmTx5bQ7Ch0+2uqRjYv7Cfb5xpX4WuSC9UgQna3sqRGBL9EImX9PvTpMaQ7g==} engines: {node: '>= 18'} + '@octokit/oauth-methods@5.1.3': + resolution: {integrity: sha512-M+bDBi5H8FnH0xhCTg0m9hvcnppdDnxUqbZyOkxlLblKpLAR+eT2nbDPvJDp0eLrvJWA1I8OX0KHf/sBMQARRA==} + engines: {node: '>= 18'} + '@octokit/openapi-types@22.2.0': resolution: {integrity: sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==} + '@octokit/openapi-types@23.0.1': + resolution: {integrity: sha512-izFjMJ1sir0jn0ldEKhZ7xegCTj/ObmEDlEfpFrx4k/JyZSMRHbO3/rBwgE7f3m2DHt+RrNGIVw4wSmwnm3t/g==} + '@octokit/openapi-webhooks-types@8.2.1': resolution: {integrity: sha512-msAU1oTSm0ZmvAE0xDemuF4tVs5i0xNnNGtNmr4EuATi+1Rn8cZDetj6NXioSf5LwnxEc209COa/WOSbjuhLUA==} + '@octokit/openapi-webhooks-types@8.5.1': + resolution: {integrity: sha512-i3h1b5zpGSB39ffBbYdSGuAd0NhBAwPyA3QV3LYi/lx4lsbZiu7u2UHgXVUR6EpvOI8REOuVh1DZTRfHoJDvuQ==} + '@octokit/plugin-paginate-graphql@5.2.2': resolution: {integrity: sha512-7znSVvlNAOJisCqAnjN1FtEziweOHSjPGAuc5W58NeGNAr/ZB57yCsjQbXDlWsVryA7hHQaEQPcBbJYFawlkyg==} engines: {node: '>= 18'} peerDependencies: '@octokit/core': '>=6' + '@octokit/plugin-paginate-graphql@5.2.4': + resolution: {integrity: sha512-pLZES1jWaOynXKHOqdnwZ5ULeVR6tVVCMm+AUbp0htdcyXDU95WbkYdU4R2ej1wKj5Tu94Mee2Ne0PjPO9cCyA==} + engines: {node: '>= 18'} + peerDependencies: + '@octokit/core': '>=6' + '@octokit/plugin-paginate-rest@11.3.1': resolution: {integrity: sha512-ryqobs26cLtM1kQxqeZui4v8FeznirUsksiA+RYemMPJ7Micju0WSkv50dBksTuZks9O5cg4wp+t8fZ/cLY56g==} engines: {node: '>= 18'} peerDependencies: '@octokit/core': '5' + '@octokit/plugin-paginate-rest@11.4.0': + resolution: {integrity: sha512-ttpGck5AYWkwMkMazNCZMqxKqIq1fJBNxBfsFwwfyYKTf914jKkLF0POMS3YkPBwp5g1c2Y4L79gDz01GhSr1g==} + engines: {node: '>= 18'} + peerDependencies: + '@octokit/core': '>=6' + '@octokit/plugin-request-log@4.0.0': resolution: {integrity: sha512-2uJI1COtYCq8Z4yNSnM231TgH50bRkheQ9+aH8TnZanB6QilOnx8RMD2qsnamSOXtDj0ilxvevf5fGsBhBBzKA==} engines: {node: '>= 18'} @@ -1133,18 +1194,36 @@ packages: peerDependencies: '@octokit/core': ^5 + '@octokit/plugin-rest-endpoint-methods@13.3.0': + resolution: {integrity: sha512-LUm44shlmkp/6VC+qQgHl3W5vzUP99ZM54zH6BuqkJK4DqfFLhegANd+fM4YRLapTvPm4049iG7F3haANKMYvQ==} + engines: {node: '>= 18'} + peerDependencies: + '@octokit/core': '>=6' + '@octokit/plugin-retry@7.1.1': resolution: {integrity: sha512-G9Ue+x2odcb8E1XIPhaFBnTTIrrUDfXN05iFXiqhR+SeeeDMMILcAnysOsxUpEWcQp2e5Ft397FCXTcPkiPkLw==} engines: {node: '>= 18'} peerDependencies: '@octokit/core': '>=6' + '@octokit/plugin-retry@7.1.3': + resolution: {integrity: sha512-8nKOXvYWnzv89gSyIvgFHmCBAxfQAOPRlkacUHL9r5oWtp5Whxl8Skb2n3ACZd+X6cYijD6uvmrQuPH/UCL5zQ==} + engines: {node: '>= 18'} + peerDependencies: + '@octokit/core': '>=6' + '@octokit/plugin-throttling@9.3.0': resolution: {integrity: sha512-B5YTToSRTzNSeEyssnrT7WwGhpIdbpV9NKIs3KyTWHX6PhpYn7gqF/+lL3BvsASBM3Sg5BAUYk7KZx5p/Ec77w==} engines: {node: '>= 18'} peerDependencies: '@octokit/core': ^6.0.0 + '@octokit/plugin-throttling@9.4.0': + resolution: {integrity: sha512-IOlXxXhZA4Z3m0EEYtrrACkuHiArHLZ3CvqWwOez/pURNqRuwfoFlTPbN5Muf28pzFuztxPyiUiNwz8KctdZaQ==} + engines: {node: '>= 18'} + peerDependencies: + '@octokit/core': ^6.1.3 + '@octokit/request-error@5.1.0': resolution: {integrity: sha512-GETXfE05J0+7H2STzekpKObFe765O5dlAKUTLNGeH+x47z7JjXHfsHKo5z21D/o/IOZTUEI6nyWyR+bZVP/n5Q==} engines: {node: '>= 18'} @@ -1153,6 +1232,10 @@ packages: resolution: {integrity: sha512-IlBTfGX8Yn/oFPMwSfvugfncK2EwRLjzbrpifNaMY8o/HTEAFqCA1FZxjD9cWvSKBHgrIhc4CSBIzMxiLsbzFQ==} engines: {node: '>= 18'} + '@octokit/request-error@6.1.6': + resolution: {integrity: sha512-pqnVKYo/at0NuOjinrgcQYpEbv4snvP3bKMRqHaD9kIsk9u1LCpb2smHZi8/qJfgeNqLo5hNW4Z7FezNdEo0xg==} + engines: {node: '>= 18'} + '@octokit/request@8.4.0': resolution: {integrity: sha512-9Bb014e+m2TgBeEJGEbdplMVWwPmL1FPtggHQRkV+WVsMggPtEkLKPlcVYm/o8xKLkpJ7B+6N8WfQMtDLX2Dpw==} engines: {node: '>= 18'} @@ -1161,6 +1244,10 @@ packages: resolution: {integrity: sha512-pyAguc0p+f+GbQho0uNetNQMmLG1e80WjkIaqqgUkihqUp0boRU6nKItXO4VWnr+nbZiLGEyy4TeKRwqaLvYgw==} engines: {node: '>= 18'} + '@octokit/request@9.1.4': + resolution: {integrity: sha512-tMbOwGm6wDII6vygP3wUVqFTw3Aoo0FnVQyhihh8vVq12uO3P+vQZeo2CKMpWtPSogpACD0yyZAlVlQnjW71DA==} + engines: {node: '>= 18'} + '@octokit/rest@20.1.1': resolution: {integrity: sha512-MB4AYDsM5jhIHro/dq4ix1iWTLGToIGk6cWF5L6vanFaMble5jTX/UBQyiv05HsWnwUtY8JrfHy2LWfKwihqMw==} engines: {node: '>= 18'} @@ -1168,6 +1255,9 @@ packages: '@octokit/types@13.5.0': resolution: {integrity: sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==} + '@octokit/types@13.7.0': + resolution: {integrity: sha512-BXfRP+3P3IN6fd4uF3SniaHKOO4UXWBfkdR3vA8mIvaoO/wLjGN5qivUtW0QRitBHHMcfC41SLhNVYIZZE+wkA==} + '@octokit/webhooks-methods@5.1.0': resolution: {integrity: sha512-yFZa3UH11VIxYnnoOYCVoJ3q4ChuSOk2IVBBQ0O3xtKX4x9bmKb/1t+Mxixv2iUhzMdOl1qeWJqEhouXXzB3rQ==} engines: {node: '>= 18'} @@ -1176,6 +1266,10 @@ packages: resolution: {integrity: sha512-sPHCyi9uZuCs1gg0yF53FFocM+GsiiBEhQQV/itGzzQ8gjyv2GMJ1YvgdDY4lC0ePZeiV3juEw4GbS6w1VHhRw==} engines: {node: '>= 18'} + '@octokit/webhooks@13.4.2': + resolution: {integrity: sha512-fakbgkCScapQXPxyqx2jZs/Y3jGlyezwUp7ATL7oLAGJ0+SqBKWKstoKZpiQ+REeHutKpYjY9UtxdLSurwl2Tg==} + engines: {node: '>= 18'} + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -1912,14 +2006,19 @@ packages: typescript: optional: true - create-testers@0.1.0-alpha.11: - resolution: {integrity: sha512-rYayi3nSVRExogBreXSR4xL5ZoeGuEoaq/NJOEJVCC1NsIR1XrkoWNsrmLUEqbYHTZ4KZWhUbXLR+pX2JEdd0w==} + create-fs@0.1.0: + resolution: {integrity: sha512-IxoO6aFIPs+loAGrBn/Hg8UzQ5pRfjtsNqSW8o/RMtjh0RShZcuKbDYWvepnjTqDAaHdbkaXFY7y86WXqQnzpA==} + engines: {node: '>=18'} + + create-testers@0.1.0-alpha.13: + resolution: {integrity: sha512-rrCLIajWABQcakAr9eLwIqmzaXVx8zyfE/dXKf2Xj/L4+7m/48JE+4q8gRLh6SYsb4c/SFlaKZ/q50gIWitDcg==} engines: {node: '>=18'} peerDependencies: - create: ^0.1.0-alpha.11 + create: ^0.1.0-alpha.13 + create-fs: ^0.1.0 - create@0.1.0-alpha.11: - resolution: {integrity: sha512-F+FTEm++rsimmCNq4zwq7dXfxAl8UA06XcfR/zS81Q9CZGYSREWEhq1HSCaHseJkAEW0uB8i3cSO/3vhlUsvOQ==} + create@0.1.0-alpha.13: + resolution: {integrity: sha512-8pX3fXtYzyXcCkMppv7R9hsw0SFbxQxzkEDsjX8WyIQd0jPuSIQMr0ofZEUthJQG63NlJCvXmS2V4H7LQDUpFA==} engines: {node: '>=18'} hasBin: true @@ -2065,6 +2164,10 @@ packages: didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + diff@7.0.0: + resolution: {integrity: sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==} + engines: {node: '>=0.3.1'} + dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} @@ -2330,6 +2433,9 @@ packages: resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} engines: {node: '>=4'} + fast-content-type-parse@2.0.1: + resolution: {integrity: sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -2637,8 +2743,8 @@ packages: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} - import-local-or-npx@0.1.0: - resolution: {integrity: sha512-AIeX2teTRbL4eIGffiqOwFlG/MVZlep49MVEtyls9J0SvHVDzqsuVEHjO29ucaj2LGPcT5Rg/wCTgTFJcWAkVQ==} + import-local-or-npx@0.2.0: + resolution: {integrity: sha512-K0rFCUuIBX7lmzwp95F4Owg+bRZAUMoiC79fYSRHPG/pi3IFKyMYrc+RE00mC9AWU6J/YZKT8xept5VyLvkvUw==} engines: {node: '>=18.3.0'} import-meta-resolve@4.1.0: @@ -3318,6 +3424,10 @@ packages: resolution: {integrity: sha512-wbqF4uc1YbcldtiBFfkSnquHtECEIpYD78YUXI6ri1Im5OO2NLo6ZVpRdbJpdnpZ05zMrVPssNiEo6JQtea+Qg==} engines: {node: '>= 18'} + octokit@4.1.0: + resolution: {integrity: sha512-/UrQAOSvkc+lUUWKNzy4ByAgYU9KpFzZQt8DnC962YmQuDiZb1SNJ90YukCCK5aMzKqqCA+z1kkAlmzYvdYKag==} + engines: {node: '>= 18'} + once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} @@ -4027,6 +4137,10 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + toad-cache@3.7.0: + resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==} + engines: {node: '>=12'} + tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} @@ -4429,14 +4543,14 @@ snapshots: '@bcoe/v8-coverage@0.2.3': {} - '@clack/core@0.4.0': + '@clack/core@0.4.1': dependencies: picocolors: 1.1.1 sisteransi: 1.0.5 - '@clack/prompts@0.9.0': + '@clack/prompts@0.9.1': dependencies: - '@clack/core': 0.4.0 + '@clack/core': 0.4.1 picocolors: 1.1.1 sisteransi: 1.0.5 @@ -4992,6 +5106,16 @@ snapshots: '@octokit/types': 13.5.0 '@octokit/webhooks': 13.2.7 + '@octokit/app@15.1.2': + dependencies: + '@octokit/auth-app': 7.1.4 + '@octokit/auth-unauthenticated': 6.1.1 + '@octokit/core': 6.1.3 + '@octokit/oauth-app': 7.1.5 + '@octokit/plugin-paginate-rest': 11.4.0(@octokit/core@6.1.3) + '@octokit/types': 13.7.0 + '@octokit/webhooks': 13.4.2 + '@octokit/auth-app@7.1.0': dependencies: '@octokit/auth-oauth-app': 8.1.1 @@ -5003,6 +5127,17 @@ snapshots: universal-github-app-jwt: 2.2.0 universal-user-agent: 7.0.2 + '@octokit/auth-app@7.1.4': + dependencies: + '@octokit/auth-oauth-app': 8.1.2 + '@octokit/auth-oauth-user': 5.1.2 + '@octokit/request': 9.1.4 + '@octokit/request-error': 6.1.6 + '@octokit/types': 13.7.0 + toad-cache: 3.7.0 + universal-github-app-jwt: 2.2.0 + universal-user-agent: 7.0.2 + '@octokit/auth-oauth-app@8.1.1': dependencies: '@octokit/auth-oauth-device': 7.1.1 @@ -5011,6 +5146,14 @@ snapshots: '@octokit/types': 13.5.0 universal-user-agent: 7.0.2 + '@octokit/auth-oauth-app@8.1.2': + dependencies: + '@octokit/auth-oauth-device': 7.1.2 + '@octokit/auth-oauth-user': 5.1.2 + '@octokit/request': 9.1.4 + '@octokit/types': 13.7.0 + universal-user-agent: 7.0.2 + '@octokit/auth-oauth-device@7.1.1': dependencies: '@octokit/oauth-methods': 5.1.2 @@ -5018,6 +5161,13 @@ snapshots: '@octokit/types': 13.5.0 universal-user-agent: 7.0.2 + '@octokit/auth-oauth-device@7.1.2': + dependencies: + '@octokit/oauth-methods': 5.1.3 + '@octokit/request': 9.1.4 + '@octokit/types': 13.7.0 + universal-user-agent: 7.0.2 + '@octokit/auth-oauth-user@5.1.1': dependencies: '@octokit/auth-oauth-device': 7.1.1 @@ -5026,6 +5176,14 @@ snapshots: '@octokit/types': 13.5.0 universal-user-agent: 7.0.2 + '@octokit/auth-oauth-user@5.1.2': + dependencies: + '@octokit/auth-oauth-device': 7.1.2 + '@octokit/oauth-methods': 5.1.3 + '@octokit/request': 9.1.4 + '@octokit/types': 13.7.0 + universal-user-agent: 7.0.2 + '@octokit/auth-token@4.0.0': {} '@octokit/auth-token@5.1.1': {} @@ -5035,6 +5193,11 @@ snapshots: '@octokit/request-error': 6.1.5 '@octokit/types': 13.5.0 + '@octokit/auth-unauthenticated@6.1.1': + dependencies: + '@octokit/request-error': 6.1.6 + '@octokit/types': 13.7.0 + '@octokit/core@5.2.0': dependencies: '@octokit/auth-token': 4.0.0 @@ -5055,6 +5218,16 @@ snapshots: before-after-hook: 3.0.2 universal-user-agent: 7.0.2 + '@octokit/core@6.1.3': + dependencies: + '@octokit/auth-token': 5.1.1 + '@octokit/graphql': 8.1.2 + '@octokit/request': 9.1.4 + '@octokit/request-error': 6.1.6 + '@octokit/types': 13.7.0 + before-after-hook: 3.0.2 + universal-user-agent: 7.0.2 + '@octokit/endpoint@10.1.1': dependencies: '@octokit/types': 13.5.0 @@ -5077,6 +5250,12 @@ snapshots: '@octokit/types': 13.5.0 universal-user-agent: 7.0.2 + '@octokit/graphql@8.1.2': + dependencies: + '@octokit/request': 9.1.4 + '@octokit/types': 13.7.0 + universal-user-agent: 7.0.2 + '@octokit/oauth-app@7.1.2': dependencies: '@octokit/auth-oauth-app': 8.1.1 @@ -5088,6 +5267,17 @@ snapshots: '@types/aws-lambda': 8.10.137 universal-user-agent: 7.0.2 + '@octokit/oauth-app@7.1.5': + dependencies: + '@octokit/auth-oauth-app': 8.1.2 + '@octokit/auth-oauth-user': 5.1.2 + '@octokit/auth-unauthenticated': 6.1.1 + '@octokit/core': 6.1.3 + '@octokit/oauth-authorization-url': 7.1.1 + '@octokit/oauth-methods': 5.1.3 + '@types/aws-lambda': 8.10.137 + universal-user-agent: 7.0.2 + '@octokit/oauth-authorization-url@7.1.1': {} '@octokit/oauth-methods@5.1.2': @@ -5097,14 +5287,29 @@ snapshots: '@octokit/request-error': 6.1.5 '@octokit/types': 13.5.0 + '@octokit/oauth-methods@5.1.3': + dependencies: + '@octokit/oauth-authorization-url': 7.1.1 + '@octokit/request': 9.1.4 + '@octokit/request-error': 6.1.6 + '@octokit/types': 13.7.0 + '@octokit/openapi-types@22.2.0': {} + '@octokit/openapi-types@23.0.1': {} + '@octokit/openapi-webhooks-types@8.2.1': {} + '@octokit/openapi-webhooks-types@8.5.1': {} + '@octokit/plugin-paginate-graphql@5.2.2(@octokit/core@6.1.2)': dependencies: '@octokit/core': 6.1.2 + '@octokit/plugin-paginate-graphql@5.2.4(@octokit/core@6.1.3)': + dependencies: + '@octokit/core': 6.1.3 + '@octokit/plugin-paginate-rest@11.3.1(@octokit/core@5.2.0)': dependencies: '@octokit/core': 5.2.0 @@ -5115,6 +5320,11 @@ snapshots: '@octokit/core': 6.1.2 '@octokit/types': 13.5.0 + '@octokit/plugin-paginate-rest@11.4.0(@octokit/core@6.1.3)': + dependencies: + '@octokit/core': 6.1.3 + '@octokit/types': 13.7.0 + '@octokit/plugin-request-log@4.0.0(@octokit/core@5.2.0)': dependencies: '@octokit/core': 5.2.0 @@ -5129,6 +5339,11 @@ snapshots: '@octokit/core': 6.1.2 '@octokit/types': 13.5.0 + '@octokit/plugin-rest-endpoint-methods@13.3.0(@octokit/core@6.1.3)': + dependencies: + '@octokit/core': 6.1.3 + '@octokit/types': 13.7.0 + '@octokit/plugin-retry@7.1.1(@octokit/core@6.1.2)': dependencies: '@octokit/core': 6.1.2 @@ -5136,12 +5351,25 @@ snapshots: '@octokit/types': 13.5.0 bottleneck: 2.19.5 + '@octokit/plugin-retry@7.1.3(@octokit/core@6.1.3)': + dependencies: + '@octokit/core': 6.1.3 + '@octokit/request-error': 6.1.6 + '@octokit/types': 13.7.0 + bottleneck: 2.19.5 + '@octokit/plugin-throttling@9.3.0(@octokit/core@6.1.2)': dependencies: '@octokit/core': 6.1.2 '@octokit/types': 13.5.0 bottleneck: 2.19.5 + '@octokit/plugin-throttling@9.4.0(@octokit/core@6.1.3)': + dependencies: + '@octokit/core': 6.1.3 + '@octokit/types': 13.7.0 + bottleneck: 2.19.5 + '@octokit/request-error@5.1.0': dependencies: '@octokit/types': 13.5.0 @@ -5152,6 +5380,10 @@ snapshots: dependencies: '@octokit/types': 13.5.0 + '@octokit/request-error@6.1.6': + dependencies: + '@octokit/types': 13.7.0 + '@octokit/request@8.4.0': dependencies: '@octokit/endpoint': 9.0.5 @@ -5166,6 +5398,14 @@ snapshots: '@octokit/types': 13.5.0 universal-user-agent: 7.0.2 + '@octokit/request@9.1.4': + dependencies: + '@octokit/endpoint': 10.1.1 + '@octokit/request-error': 6.1.6 + '@octokit/types': 13.7.0 + fast-content-type-parse: 2.0.1 + universal-user-agent: 7.0.2 + '@octokit/rest@20.1.1': dependencies: '@octokit/core': 5.2.0 @@ -5177,6 +5417,10 @@ snapshots: dependencies: '@octokit/openapi-types': 22.2.0 + '@octokit/types@13.7.0': + dependencies: + '@octokit/openapi-types': 23.0.1 + '@octokit/webhooks-methods@5.1.0': {} '@octokit/webhooks@13.2.7': @@ -5186,6 +5430,12 @@ snapshots: '@octokit/webhooks-methods': 5.1.0 aggregate-error: 5.0.0 + '@octokit/webhooks@13.4.2': + dependencies: + '@octokit/openapi-webhooks-types': 8.5.1 + '@octokit/request-error': 6.1.6 + '@octokit/webhooks-methods': 5.1.0 + '@pkgjs/parseargs@0.11.0': optional: true @@ -5929,31 +6179,32 @@ snapshots: optionalDependencies: typescript: 5.7.2 - create-testers@0.1.0-alpha.11(create@0.1.0-alpha.11(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.0.0)(typescript@5.7.2)): + create-fs@0.1.0: {} + + create-testers@0.1.0-alpha.13(create-fs@0.1.0)(create@0.1.0-alpha.13): dependencies: - create: 0.1.0-alpha.11(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.0.0)(typescript@5.7.2) - octokit: 4.0.2 + create: 0.1.0-alpha.13 + create-fs: 0.1.0 + diff: 7.0.0 + octokit: 4.1.0 + without-undefined-properties: 0.1.1 - create@0.1.0-alpha.11(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.0.0)(typescript@5.7.2): + create@0.1.0-alpha.13: dependencies: - '@clack/prompts': 0.9.0 + '@clack/prompts': 0.9.1 cached-factory: 0.1.0 chalk: 5.4.1 + create-fs: 0.1.0 execa: 9.5.2 get-github-auth-token: 0.1.0 hash-object: 5.0.1 hosted-git-info: 8.0.2 - import-local-or-npx: 0.1.0(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.0.0)(typescript@5.7.2) - octokit: 4.0.2 + import-local-or-npx: 0.2.0 + octokit: 4.1.0 prettier: 3.4.2 read-pkg: 9.0.1 without-undefined-properties: 0.1.1 zod: 3.24.1 - transitivePeerDependencies: - - conventional-commits-filter - - conventional-commits-parser - - supports-color - - typescript cross-spawn@7.0.6: dependencies: @@ -6123,6 +6374,8 @@ snapshots: didyoumean@1.2.2: {} + diff@7.0.0: {} + dom-serializer@2.0.0: dependencies: domelementtype: 2.3.0 @@ -6552,6 +6805,8 @@ snapshots: iconv-lite: 0.4.24 tmp: 0.0.33 + fast-content-type-parse@2.0.1: {} + fast-deep-equal@3.1.3: {} fast-equals@5.2.2: {} @@ -6868,17 +7123,10 @@ snapshots: parent-module: 1.0.1 resolve-from: 4.0.0 - import-local-or-npx@0.1.0(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.0.0)(typescript@5.7.2): + import-local-or-npx@0.2.0: dependencies: - '@release-it/conventional-changelog': 9.0.3(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.0.0)(release-it@17.10.0(typescript@5.7.2)) enhanced-resolve: 5.18.0 npx-import: 1.1.4 - release-it: 17.10.0(typescript@5.7.2) - transitivePeerDependencies: - - conventional-commits-filter - - conventional-commits-parser - - supports-color - - typescript import-meta-resolve@4.1.0: {} @@ -6901,20 +7149,20 @@ snapshots: ini@4.1.1: {} - input-from-file-json@0.1.0-alpha.4(create@0.1.0-alpha.11(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.0.0)(typescript@5.7.2)): + input-from-file-json@0.1.0-alpha.4(create@0.1.0-alpha.13): dependencies: - create: 0.1.0-alpha.11(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.0.0)(typescript@5.7.2) - input-from-file: 0.1.0-alpha.4(create@0.1.0-alpha.11(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.0.0)(typescript@5.7.2)) + create: 0.1.0-alpha.13 + input-from-file: 0.1.0-alpha.4(create@0.1.0-alpha.13) zod: 3.24.1 - input-from-file@0.1.0-alpha.4(create@0.1.0-alpha.11(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.0.0)(typescript@5.7.2)): + input-from-file@0.1.0-alpha.4(create@0.1.0-alpha.13): dependencies: - create: 0.1.0-alpha.11(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.0.0)(typescript@5.7.2) + create: 0.1.0-alpha.13 zod: 3.24.1 - input-from-script@0.1.0-alpha.4(create@0.1.0-alpha.11(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.0.0)(typescript@5.7.2)): + input-from-script@0.1.0-alpha.4(create@0.1.0-alpha.13): dependencies: - create: 0.1.0-alpha.11(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.0.0)(typescript@5.7.2) + create: 0.1.0-alpha.13 zod: 3.24.1 inquirer@7.3.3: @@ -7653,6 +7901,19 @@ snapshots: '@octokit/request-error': 6.1.5 '@octokit/types': 13.5.0 + octokit@4.1.0: + dependencies: + '@octokit/app': 15.1.2 + '@octokit/core': 6.1.3 + '@octokit/oauth-app': 7.1.5 + '@octokit/plugin-paginate-graphql': 5.2.4(@octokit/core@6.1.3) + '@octokit/plugin-paginate-rest': 11.4.0(@octokit/core@6.1.3) + '@octokit/plugin-rest-endpoint-methods': 13.3.0(@octokit/core@6.1.3) + '@octokit/plugin-retry': 7.1.3(@octokit/core@6.1.3) + '@octokit/plugin-throttling': 9.4.0(@octokit/core@6.1.3) + '@octokit/request-error': 6.1.6 + '@octokit/types': 13.7.0 + once@1.4.0: dependencies: wrappy: 1.0.2 @@ -8398,6 +8659,8 @@ snapshots: dependencies: is-number: 7.0.0 + toad-cache@3.7.0: {} + tr46@0.0.3: {} tr46@1.0.1: diff --git a/src/blocks/blockRepositoryBranchRuleset.ts b/src/blocks/blockRepositoryBranchRuleset.ts index 0c26e380b..652b73931 100644 --- a/src/blocks/blockRepositoryBranchRuleset.ts +++ b/src/blocks/blockRepositoryBranchRuleset.ts @@ -39,7 +39,6 @@ export const blockRepositoryBranchRuleset = base.createBlock({ { type: "deletion" }, { parameters: { - // @ts-expect-error -- https://github.com/github/rest-api-description/issues/4405 allowed_merge_methods: ["squash"], dismiss_stale_reviews_on_push: false, require_code_owner_review: false, From 2a2932c3b7a8a8bae529900a043af7ab863d157b Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Tue, 14 Jan 2025 13:56:01 -0500 Subject: [PATCH 04/22] Update pnpm-lock.yaml --- pnpm-lock.yaml | 281 ++----------------------------------------------- 1 file changed, 10 insertions(+), 271 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c0fca1afb..2ec3c1629 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1038,42 +1038,22 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@octokit/app@15.0.1': - resolution: {integrity: sha512-nwSjC349E6/wruMCo944y1dBC7uKzUYrBMoC4Qx/xfLLBmD+R66oMKB1jXS2HYRF9Hqh/Alq3UNRggVWZxjvUg==} - engines: {node: '>= 18'} - '@octokit/app@15.1.2': resolution: {integrity: sha512-6aKmKvqnJKoVK+kx0mLlBMKmQYoziPw4Rd/PWr0j65QVQlrDXlu6hGU8fmTXt7tNkf/DsubdIaTT4fkoWzCh5g==} engines: {node: '>= 18'} - '@octokit/auth-app@7.1.0': - resolution: {integrity: sha512-cazGaJPSgeZ8NkVYeM/C5l/6IQ5vZnsI8p1aMucadCkt/bndI+q+VqwrlnWbASRmenjOkf1t1RpCKrif53U8gw==} - engines: {node: '>= 18'} - '@octokit/auth-app@7.1.4': resolution: {integrity: sha512-5F+3l/maq9JfWQ4bV28jT2G/K8eu9OJ317yzXPTGe4Kw+lKDhFaS4dQ3Ltmb6xImKxfCQdqDqMXODhc9YLipLw==} engines: {node: '>= 18'} - '@octokit/auth-oauth-app@8.1.1': - resolution: {integrity: sha512-5UtmxXAvU2wfcHIPPDWzVSAWXVJzG3NWsxb7zCFplCWEmMCArSZV0UQu5jw5goLQXbFyOr5onzEH37UJB3zQQg==} - engines: {node: '>= 18'} - '@octokit/auth-oauth-app@8.1.2': resolution: {integrity: sha512-3woNZgq5/S6RS+9ZTq+JdymxVr7E0s4EYxF20ugQvgX3pomdPUL5r/XdTY9wALoBM2eHVy4ettr5fKpatyTyHw==} engines: {node: '>= 18'} - '@octokit/auth-oauth-device@7.1.1': - resolution: {integrity: sha512-HWl8lYueHonuyjrKKIup/1tiy0xcmQCdq5ikvMO1YwkNNkxb6DXfrPjrMYItNLyCP/o2H87WuijuE+SlBTT8eg==} - engines: {node: '>= 18'} - '@octokit/auth-oauth-device@7.1.2': resolution: {integrity: sha512-gTOIzDeV36OhVfxCl69FmvJix7tJIiU6dlxuzLVAzle7fYfO8UDyddr9B+o4CFQVaMBLMGZ9ak2CWMYcGeZnPw==} engines: {node: '>= 18'} - '@octokit/auth-oauth-user@5.1.1': - resolution: {integrity: sha512-rRkMz0ErOppdvEfnemHJXgZ9vTPhBuC6yASeFaB7I2yLMd7QpjfrL1mnvRPlyKo+M6eeLxrKanXJ9Qte29SRsw==} - engines: {node: '>= 18'} - '@octokit/auth-oauth-user@5.1.2': resolution: {integrity: sha512-PgVDDPJgZYb3qSEXK4moksA23tfn68zwSAsQKZ1uH6IV9IaNEYx35OXXI80STQaLYnmEE86AgU0tC1YkM4WjsA==} engines: {node: '>= 18'} @@ -1086,10 +1066,6 @@ packages: resolution: {integrity: sha512-rh3G3wDO8J9wSjfI436JUKzHIxq8NaiL0tVeB2aXmG6p/9859aUOAjA9pmSPNGGZxfwmaJ9ozOJImuNVJdpvbA==} engines: {node: '>= 18'} - '@octokit/auth-unauthenticated@6.1.0': - resolution: {integrity: sha512-zPSmfrUAcspZH/lOFQnVnvjQZsIvmfApQH6GzJrkIunDooU1Su2qt2FfMTSVPRp7WLTQyC20Kd55lF+mIYaohQ==} - engines: {node: '>= 18'} - '@octokit/auth-unauthenticated@6.1.1': resolution: {integrity: sha512-bGXqdN6RhSFHvpPq46SL8sN+F3odQ6oMNLWc875IgoqcC3qus+fOL2th6Tkl94wvdSTy8/OeHzWy/lZebmnhog==} engines: {node: '>= 18'} @@ -1098,10 +1074,6 @@ packages: resolution: {integrity: sha512-1LFfa/qnMQvEOAdzlQymH0ulepxbxnCYAKJZfMci/5XJyIHWgEYnDmgnKakbTh7CH2tFQ5O60oYDvns4i9RAIg==} engines: {node: '>= 18'} - '@octokit/core@6.1.2': - resolution: {integrity: sha512-hEb7Ma4cGJGEUNOAVmyfdB/3WirWMg5hDuNFVejGEDFqupeOysLc2sG6HJxY2etBp5YQu5Wtxwi020jS9xlUwg==} - engines: {node: '>= 18'} - '@octokit/core@6.1.3': resolution: {integrity: sha512-z+j7DixNnfpdToYsOutStDgeRzJSMnbj8T1C/oQjB6Aa+kRfNjs/Fn7W6c8bmlt6mfy3FkgeKBRnDjxQow5dow==} engines: {node: '>= 18'} @@ -1118,18 +1090,10 @@ packages: resolution: {integrity: sha512-r+oZUH7aMFui1ypZnAvZmn0KSqAUgE1/tUXIWaqUCa1758ts/Jio84GZuzsvUkme98kv0WFY8//n0J1Z+vsIsQ==} engines: {node: '>= 18'} - '@octokit/graphql@8.1.1': - resolution: {integrity: sha512-ukiRmuHTi6ebQx/HFRCXKbDlOh/7xEV6QUXaE7MJEKGNAncGI/STSbOkl12qVXZrfZdpXctx5O9X1AIaebiDBg==} - engines: {node: '>= 18'} - '@octokit/graphql@8.1.2': resolution: {integrity: sha512-bdlj/CJVjpaz06NBpfHhp4kGJaRZfz7AzC+6EwUImRtrwIw8dIgJ63Xg0OzV9pRn3rIzrt5c2sa++BL0JJ8GLw==} engines: {node: '>= 18'} - '@octokit/oauth-app@7.1.2': - resolution: {integrity: sha512-4ntCOZIiTozKwuYQroX/ZD722tzMH8Eicv/cgDM/3F3lyrlwENHDv4flTCBpSJbfK546B2SrkKMWB+/HbS84zQ==} - engines: {node: '>= 18'} - '@octokit/oauth-app@7.1.5': resolution: {integrity: sha512-/Y2MiwWDlGUK4blKKfjJiwjzu/FzwKTTTfTZAAQ0QbdBIDEGJPWhOFH6muSN86zaa4tNheB4YS3oWIR2e4ydzA==} engines: {node: '>= 18'} @@ -1138,32 +1102,16 @@ packages: resolution: {integrity: sha512-ooXV8GBSabSWyhLUowlMIVd9l1s2nsOGQdlP2SQ4LnkEsGXzeCvbSbCPdZThXhEFzleGPwbapT0Sb+YhXRyjCA==} engines: {node: '>= 18'} - '@octokit/oauth-methods@5.1.2': - resolution: {integrity: sha512-C5lglRD+sBlbrhCUTxgJAFjWgJlmTx5bQ7Ch0+2uqRjYv7Cfb5xpX4WuSC9UgQna3sqRGBL9EImX9PvTpMaQ7g==} - engines: {node: '>= 18'} - '@octokit/oauth-methods@5.1.3': resolution: {integrity: sha512-M+bDBi5H8FnH0xhCTg0m9hvcnppdDnxUqbZyOkxlLblKpLAR+eT2nbDPvJDp0eLrvJWA1I8OX0KHf/sBMQARRA==} engines: {node: '>= 18'} - '@octokit/openapi-types@22.2.0': - resolution: {integrity: sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==} - '@octokit/openapi-types@23.0.1': resolution: {integrity: sha512-izFjMJ1sir0jn0ldEKhZ7xegCTj/ObmEDlEfpFrx4k/JyZSMRHbO3/rBwgE7f3m2DHt+RrNGIVw4wSmwnm3t/g==} - '@octokit/openapi-webhooks-types@8.2.1': - resolution: {integrity: sha512-msAU1oTSm0ZmvAE0xDemuF4tVs5i0xNnNGtNmr4EuATi+1Rn8cZDetj6NXioSf5LwnxEc209COa/WOSbjuhLUA==} - '@octokit/openapi-webhooks-types@8.5.1': resolution: {integrity: sha512-i3h1b5zpGSB39ffBbYdSGuAd0NhBAwPyA3QV3LYi/lx4lsbZiu7u2UHgXVUR6EpvOI8REOuVh1DZTRfHoJDvuQ==} - '@octokit/plugin-paginate-graphql@5.2.2': - resolution: {integrity: sha512-7znSVvlNAOJisCqAnjN1FtEziweOHSjPGAuc5W58NeGNAr/ZB57yCsjQbXDlWsVryA7hHQaEQPcBbJYFawlkyg==} - engines: {node: '>= 18'} - peerDependencies: - '@octokit/core': '>=6' - '@octokit/plugin-paginate-graphql@5.2.4': resolution: {integrity: sha512-pLZES1jWaOynXKHOqdnwZ5ULeVR6tVVCMm+AUbp0htdcyXDU95WbkYdU4R2ej1wKj5Tu94Mee2Ne0PjPO9cCyA==} engines: {node: '>= 18'} @@ -1200,24 +1148,12 @@ packages: peerDependencies: '@octokit/core': '>=6' - '@octokit/plugin-retry@7.1.1': - resolution: {integrity: sha512-G9Ue+x2odcb8E1XIPhaFBnTTIrrUDfXN05iFXiqhR+SeeeDMMILcAnysOsxUpEWcQp2e5Ft397FCXTcPkiPkLw==} - engines: {node: '>= 18'} - peerDependencies: - '@octokit/core': '>=6' - '@octokit/plugin-retry@7.1.3': resolution: {integrity: sha512-8nKOXvYWnzv89gSyIvgFHmCBAxfQAOPRlkacUHL9r5oWtp5Whxl8Skb2n3ACZd+X6cYijD6uvmrQuPH/UCL5zQ==} engines: {node: '>= 18'} peerDependencies: '@octokit/core': '>=6' - '@octokit/plugin-throttling@9.3.0': - resolution: {integrity: sha512-B5YTToSRTzNSeEyssnrT7WwGhpIdbpV9NKIs3KyTWHX6PhpYn7gqF/+lL3BvsASBM3Sg5BAUYk7KZx5p/Ec77w==} - engines: {node: '>= 18'} - peerDependencies: - '@octokit/core': ^6.0.0 - '@octokit/plugin-throttling@9.4.0': resolution: {integrity: sha512-IOlXxXhZA4Z3m0EEYtrrACkuHiArHLZ3CvqWwOez/pURNqRuwfoFlTPbN5Muf28pzFuztxPyiUiNwz8KctdZaQ==} engines: {node: '>= 18'} @@ -1228,10 +1164,6 @@ packages: resolution: {integrity: sha512-GETXfE05J0+7H2STzekpKObFe765O5dlAKUTLNGeH+x47z7JjXHfsHKo5z21D/o/IOZTUEI6nyWyR+bZVP/n5Q==} engines: {node: '>= 18'} - '@octokit/request-error@6.1.5': - resolution: {integrity: sha512-IlBTfGX8Yn/oFPMwSfvugfncK2EwRLjzbrpifNaMY8o/HTEAFqCA1FZxjD9cWvSKBHgrIhc4CSBIzMxiLsbzFQ==} - engines: {node: '>= 18'} - '@octokit/request-error@6.1.6': resolution: {integrity: sha512-pqnVKYo/at0NuOjinrgcQYpEbv4snvP3bKMRqHaD9kIsk9u1LCpb2smHZi8/qJfgeNqLo5hNW4Z7FezNdEo0xg==} engines: {node: '>= 18'} @@ -1240,10 +1172,6 @@ packages: resolution: {integrity: sha512-9Bb014e+m2TgBeEJGEbdplMVWwPmL1FPtggHQRkV+WVsMggPtEkLKPlcVYm/o8xKLkpJ7B+6N8WfQMtDLX2Dpw==} engines: {node: '>= 18'} - '@octokit/request@9.1.1': - resolution: {integrity: sha512-pyAguc0p+f+GbQho0uNetNQMmLG1e80WjkIaqqgUkihqUp0boRU6nKItXO4VWnr+nbZiLGEyy4TeKRwqaLvYgw==} - engines: {node: '>= 18'} - '@octokit/request@9.1.4': resolution: {integrity: sha512-tMbOwGm6wDII6vygP3wUVqFTw3Aoo0FnVQyhihh8vVq12uO3P+vQZeo2CKMpWtPSogpACD0yyZAlVlQnjW71DA==} engines: {node: '>= 18'} @@ -1252,9 +1180,6 @@ packages: resolution: {integrity: sha512-MB4AYDsM5jhIHro/dq4ix1iWTLGToIGk6cWF5L6vanFaMble5jTX/UBQyiv05HsWnwUtY8JrfHy2LWfKwihqMw==} engines: {node: '>= 18'} - '@octokit/types@13.5.0': - resolution: {integrity: sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==} - '@octokit/types@13.7.0': resolution: {integrity: sha512-BXfRP+3P3IN6fd4uF3SniaHKOO4UXWBfkdR3vA8mIvaoO/wLjGN5qivUtW0QRitBHHMcfC41SLhNVYIZZE+wkA==} @@ -1262,10 +1187,6 @@ packages: resolution: {integrity: sha512-yFZa3UH11VIxYnnoOYCVoJ3q4ChuSOk2IVBBQ0O3xtKX4x9bmKb/1t+Mxixv2iUhzMdOl1qeWJqEhouXXzB3rQ==} engines: {node: '>= 18'} - '@octokit/webhooks@13.2.7': - resolution: {integrity: sha512-sPHCyi9uZuCs1gg0yF53FFocM+GsiiBEhQQV/itGzzQ8gjyv2GMJ1YvgdDY4lC0ePZeiV3juEw4GbS6w1VHhRw==} - engines: {node: '>= 18'} - '@octokit/webhooks@13.4.2': resolution: {integrity: sha512-fakbgkCScapQXPxyqx2jZs/Y3jGlyezwUp7ATL7oLAGJ0+SqBKWKstoKZpiQ+REeHutKpYjY9UtxdLSurwl2Tg==} engines: {node: '>= 18'} @@ -1589,10 +1510,6 @@ packages: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} engines: {node: '>=8'} - aggregate-error@5.0.0: - resolution: {integrity: sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==} - engines: {node: '>=18'} - ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} @@ -1815,10 +1732,6 @@ packages: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} - clean-stack@5.2.0: - resolution: {integrity: sha512-TyUIUJgdFnCISzG5zu3291TAsE77ddchd0bepon1VVQrKLGKFED4iXFEDQ24mIPdPBbyE16PK3F8MYE1CmcBEQ==} - engines: {node: '>=14.16'} - clear-module@4.1.2: resolution: {integrity: sha512-LWAxzHqdHsAZlPlEyJ2Poz6AIs384mPeqLVCru2p0BrP9G/kVGuhNyZYClLO6cXlnuJjzC8xtsJIuMjKqLXoAw==} engines: {node: '>=8'} @@ -2261,10 +2174,6 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - escape-string-regexp@5.0.0: - resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} - engines: {node: '>=12'} - escodegen@2.1.0: resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} engines: {node: '>=6.0'} @@ -2758,10 +2667,6 @@ packages: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} - indent-string@5.0.0: - resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} - engines: {node: '>=12'} - index-to-position@0.1.2: resolution: {integrity: sha512-MWDKS3AS1bGCHLBA2VLImJz42f7bJh8wQsTGCzI3j519/CASStoDONUBVz2I/VID0MpiX3SGSnbOD2xUalbE5g==} engines: {node: '>=18'} @@ -3420,10 +3325,6 @@ packages: resolution: {integrity: sha512-UMo+WQkWi1PS/HZ15SMM1sgPhhXykqPJKCCAi6R0+JAhTbLBcfeKCJ3AwUVdJBJQKSQ37BUqPpME4iGAjZQtkw==} engines: {node: '>=18.3.0'} - octokit@4.0.2: - resolution: {integrity: sha512-wbqF4uc1YbcldtiBFfkSnquHtECEIpYD78YUXI6ri1Im5OO2NLo6ZVpRdbJpdnpZ05zMrVPssNiEo6JQtea+Qg==} - engines: {node: '>= 18'} - octokit@4.1.0: resolution: {integrity: sha512-/UrQAOSvkc+lUUWKNzy4ByAgYU9KpFzZQt8DnC962YmQuDiZb1SNJ90YukCCK5aMzKqqCA+z1kkAlmzYvdYKag==} engines: {node: '>= 18'} @@ -5096,16 +4997,6 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.15.0 - '@octokit/app@15.0.1': - dependencies: - '@octokit/auth-app': 7.1.0 - '@octokit/auth-unauthenticated': 6.1.0 - '@octokit/core': 6.1.2 - '@octokit/oauth-app': 7.1.2 - '@octokit/plugin-paginate-rest': 11.3.1(@octokit/core@6.1.2) - '@octokit/types': 13.5.0 - '@octokit/webhooks': 13.2.7 - '@octokit/app@15.1.2': dependencies: '@octokit/auth-app': 7.1.4 @@ -5116,17 +5007,6 @@ snapshots: '@octokit/types': 13.7.0 '@octokit/webhooks': 13.4.2 - '@octokit/auth-app@7.1.0': - dependencies: - '@octokit/auth-oauth-app': 8.1.1 - '@octokit/auth-oauth-user': 5.1.1 - '@octokit/request': 9.1.1 - '@octokit/request-error': 6.1.5 - '@octokit/types': 13.5.0 - lru-cache: 10.4.3 - universal-github-app-jwt: 2.2.0 - universal-user-agent: 7.0.2 - '@octokit/auth-app@7.1.4': dependencies: '@octokit/auth-oauth-app': 8.1.2 @@ -5138,14 +5018,6 @@ snapshots: universal-github-app-jwt: 2.2.0 universal-user-agent: 7.0.2 - '@octokit/auth-oauth-app@8.1.1': - dependencies: - '@octokit/auth-oauth-device': 7.1.1 - '@octokit/auth-oauth-user': 5.1.1 - '@octokit/request': 9.1.1 - '@octokit/types': 13.5.0 - universal-user-agent: 7.0.2 - '@octokit/auth-oauth-app@8.1.2': dependencies: '@octokit/auth-oauth-device': 7.1.2 @@ -5154,13 +5026,6 @@ snapshots: '@octokit/types': 13.7.0 universal-user-agent: 7.0.2 - '@octokit/auth-oauth-device@7.1.1': - dependencies: - '@octokit/oauth-methods': 5.1.2 - '@octokit/request': 9.1.1 - '@octokit/types': 13.5.0 - universal-user-agent: 7.0.2 - '@octokit/auth-oauth-device@7.1.2': dependencies: '@octokit/oauth-methods': 5.1.3 @@ -5168,14 +5033,6 @@ snapshots: '@octokit/types': 13.7.0 universal-user-agent: 7.0.2 - '@octokit/auth-oauth-user@5.1.1': - dependencies: - '@octokit/auth-oauth-device': 7.1.1 - '@octokit/oauth-methods': 5.1.2 - '@octokit/request': 9.1.1 - '@octokit/types': 13.5.0 - universal-user-agent: 7.0.2 - '@octokit/auth-oauth-user@5.1.2': dependencies: '@octokit/auth-oauth-device': 7.1.2 @@ -5188,11 +5045,6 @@ snapshots: '@octokit/auth-token@5.1.1': {} - '@octokit/auth-unauthenticated@6.1.0': - dependencies: - '@octokit/request-error': 6.1.5 - '@octokit/types': 13.5.0 - '@octokit/auth-unauthenticated@6.1.1': dependencies: '@octokit/request-error': 6.1.6 @@ -5204,20 +5056,10 @@ snapshots: '@octokit/graphql': 7.1.0 '@octokit/request': 8.4.0 '@octokit/request-error': 5.1.0 - '@octokit/types': 13.5.0 + '@octokit/types': 13.7.0 before-after-hook: 2.2.3 universal-user-agent: 6.0.1 - '@octokit/core@6.1.2': - dependencies: - '@octokit/auth-token': 5.1.1 - '@octokit/graphql': 8.1.1 - '@octokit/request': 9.1.1 - '@octokit/request-error': 6.1.5 - '@octokit/types': 13.5.0 - before-after-hook: 3.0.2 - universal-user-agent: 7.0.2 - '@octokit/core@6.1.3': dependencies: '@octokit/auth-token': 5.1.1 @@ -5230,43 +5072,26 @@ snapshots: '@octokit/endpoint@10.1.1': dependencies: - '@octokit/types': 13.5.0 + '@octokit/types': 13.7.0 universal-user-agent: 7.0.2 '@octokit/endpoint@9.0.5': dependencies: - '@octokit/types': 13.5.0 + '@octokit/types': 13.7.0 universal-user-agent: 6.0.1 '@octokit/graphql@7.1.0': dependencies: '@octokit/request': 8.4.0 - '@octokit/types': 13.5.0 + '@octokit/types': 13.7.0 universal-user-agent: 6.0.1 - '@octokit/graphql@8.1.1': - dependencies: - '@octokit/request': 9.1.1 - '@octokit/types': 13.5.0 - universal-user-agent: 7.0.2 - '@octokit/graphql@8.1.2': dependencies: '@octokit/request': 9.1.4 '@octokit/types': 13.7.0 universal-user-agent: 7.0.2 - '@octokit/oauth-app@7.1.2': - dependencies: - '@octokit/auth-oauth-app': 8.1.1 - '@octokit/auth-oauth-user': 5.1.1 - '@octokit/auth-unauthenticated': 6.1.0 - '@octokit/core': 6.1.2 - '@octokit/oauth-authorization-url': 7.1.1 - '@octokit/oauth-methods': 5.1.2 - '@types/aws-lambda': 8.10.137 - universal-user-agent: 7.0.2 - '@octokit/oauth-app@7.1.5': dependencies: '@octokit/auth-oauth-app': 8.1.2 @@ -5280,13 +5105,6 @@ snapshots: '@octokit/oauth-authorization-url@7.1.1': {} - '@octokit/oauth-methods@5.1.2': - dependencies: - '@octokit/oauth-authorization-url': 7.1.1 - '@octokit/request': 9.1.1 - '@octokit/request-error': 6.1.5 - '@octokit/types': 13.5.0 - '@octokit/oauth-methods@5.1.3': dependencies: '@octokit/oauth-authorization-url': 7.1.1 @@ -5294,18 +5112,10 @@ snapshots: '@octokit/request-error': 6.1.6 '@octokit/types': 13.7.0 - '@octokit/openapi-types@22.2.0': {} - '@octokit/openapi-types@23.0.1': {} - '@octokit/openapi-webhooks-types@8.2.1': {} - '@octokit/openapi-webhooks-types@8.5.1': {} - '@octokit/plugin-paginate-graphql@5.2.2(@octokit/core@6.1.2)': - dependencies: - '@octokit/core': 6.1.2 - '@octokit/plugin-paginate-graphql@5.2.4(@octokit/core@6.1.3)': dependencies: '@octokit/core': 6.1.3 @@ -5313,12 +5123,7 @@ snapshots: '@octokit/plugin-paginate-rest@11.3.1(@octokit/core@5.2.0)': dependencies: '@octokit/core': 5.2.0 - '@octokit/types': 13.5.0 - - '@octokit/plugin-paginate-rest@11.3.1(@octokit/core@6.1.2)': - dependencies: - '@octokit/core': 6.1.2 - '@octokit/types': 13.5.0 + '@octokit/types': 13.7.0 '@octokit/plugin-paginate-rest@11.4.0(@octokit/core@6.1.3)': dependencies: @@ -5332,25 +5137,13 @@ snapshots: '@octokit/plugin-rest-endpoint-methods@13.2.2(@octokit/core@5.2.0)': dependencies: '@octokit/core': 5.2.0 - '@octokit/types': 13.5.0 - - '@octokit/plugin-rest-endpoint-methods@13.2.2(@octokit/core@6.1.2)': - dependencies: - '@octokit/core': 6.1.2 - '@octokit/types': 13.5.0 + '@octokit/types': 13.7.0 '@octokit/plugin-rest-endpoint-methods@13.3.0(@octokit/core@6.1.3)': dependencies: '@octokit/core': 6.1.3 '@octokit/types': 13.7.0 - '@octokit/plugin-retry@7.1.1(@octokit/core@6.1.2)': - dependencies: - '@octokit/core': 6.1.2 - '@octokit/request-error': 6.1.5 - '@octokit/types': 13.5.0 - bottleneck: 2.19.5 - '@octokit/plugin-retry@7.1.3(@octokit/core@6.1.3)': dependencies: '@octokit/core': 6.1.3 @@ -5358,12 +5151,6 @@ snapshots: '@octokit/types': 13.7.0 bottleneck: 2.19.5 - '@octokit/plugin-throttling@9.3.0(@octokit/core@6.1.2)': - dependencies: - '@octokit/core': 6.1.2 - '@octokit/types': 13.5.0 - bottleneck: 2.19.5 - '@octokit/plugin-throttling@9.4.0(@octokit/core@6.1.3)': dependencies: '@octokit/core': 6.1.3 @@ -5372,14 +5159,10 @@ snapshots: '@octokit/request-error@5.1.0': dependencies: - '@octokit/types': 13.5.0 + '@octokit/types': 13.7.0 deprecation: 2.3.1 once: 1.4.0 - '@octokit/request-error@6.1.5': - dependencies: - '@octokit/types': 13.5.0 - '@octokit/request-error@6.1.6': dependencies: '@octokit/types': 13.7.0 @@ -5388,16 +5171,9 @@ snapshots: dependencies: '@octokit/endpoint': 9.0.5 '@octokit/request-error': 5.1.0 - '@octokit/types': 13.5.0 + '@octokit/types': 13.7.0 universal-user-agent: 6.0.1 - '@octokit/request@9.1.1': - dependencies: - '@octokit/endpoint': 10.1.1 - '@octokit/request-error': 6.1.5 - '@octokit/types': 13.5.0 - universal-user-agent: 7.0.2 - '@octokit/request@9.1.4': dependencies: '@octokit/endpoint': 10.1.1 @@ -5413,23 +5189,12 @@ snapshots: '@octokit/plugin-request-log': 4.0.0(@octokit/core@5.2.0) '@octokit/plugin-rest-endpoint-methods': 13.2.2(@octokit/core@5.2.0) - '@octokit/types@13.5.0': - dependencies: - '@octokit/openapi-types': 22.2.0 - '@octokit/types@13.7.0': dependencies: '@octokit/openapi-types': 23.0.1 '@octokit/webhooks-methods@5.1.0': {} - '@octokit/webhooks@13.2.7': - dependencies: - '@octokit/openapi-webhooks-types': 8.2.1 - '@octokit/request-error': 6.1.5 - '@octokit/webhooks-methods': 5.1.0 - aggregate-error: 5.0.0 - '@octokit/webhooks@13.4.2': dependencies: '@octokit/openapi-webhooks-types': 8.5.1 @@ -5755,11 +5520,6 @@ snapshots: clean-stack: 2.2.0 indent-string: 4.0.0 - aggregate-error@5.0.0: - dependencies: - clean-stack: 5.2.0 - indent-string: 5.0.0 - ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -5983,10 +5743,6 @@ snapshots: clean-stack@2.2.0: {} - clean-stack@5.2.0: - dependencies: - escape-string-regexp: 5.0.0 - clear-module@4.1.2: dependencies: parent-module: 2.0.0 @@ -6524,8 +6280,6 @@ snapshots: escape-string-regexp@4.0.0: {} - escape-string-regexp@5.0.0: {} - escodegen@2.1.0: dependencies: esprima: 4.0.1 @@ -7134,8 +6888,6 @@ snapshots: indent-string@4.0.0: {} - indent-string@5.0.0: {} - index-to-position@0.1.2: {} inflight@1.0.6: @@ -7886,20 +7638,7 @@ snapshots: octokit-from-auth@0.2.0: dependencies: get-github-auth-token: 0.1.0 - octokit: 4.0.2 - - octokit@4.0.2: - dependencies: - '@octokit/app': 15.0.1 - '@octokit/core': 6.1.2 - '@octokit/oauth-app': 7.1.2 - '@octokit/plugin-paginate-graphql': 5.2.2(@octokit/core@6.1.2) - '@octokit/plugin-paginate-rest': 11.3.1(@octokit/core@6.1.2) - '@octokit/plugin-rest-endpoint-methods': 13.2.2(@octokit/core@6.1.2) - '@octokit/plugin-retry': 7.1.1(@octokit/core@6.1.2) - '@octokit/plugin-throttling': 9.3.0(@octokit/core@6.1.2) - '@octokit/request-error': 6.1.5 - '@octokit/types': 13.5.0 + octokit: 4.1.0 octokit@4.1.0: dependencies: @@ -8939,7 +8678,7 @@ snapshots: dependencies: eslint-visitor-keys: 3.4.3 lodash: 4.17.21 - yaml: 2.6.1 + yaml: 2.7.0 yaml@2.6.1: {} From c835c97a70b876f2504ad36af288ac85b246ff1b Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Tue, 14 Jan 2025 14:39:38 -0500 Subject: [PATCH 05/22] test: smaller lines in docs.test.ts --- src/docs.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/docs.test.ts b/src/docs.test.ts index 6134303c1..0c1273c02 100644 --- a/src/docs.test.ts +++ b/src/docs.test.ts @@ -54,8 +54,8 @@ async function createActualLines() { async function createExpectedLines() { const lines = [ - "| Block | Exclusion Flag | Minimal | Common | Everything |", - "| ---------------------------- | ---------------------------------------- | ------- | ------ | ---------- |", + "| Block | Exclusion Flag | Minimal | Common | Everything |", + "| ----- | -------------- | ------- | ------ | ---------- |", ]; for (const block of Object.values(blocks) as Block[]) { From 06f0aaa1c796d62dffeb8ae0a621f75562350125 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 15 Jan 2025 08:37:57 -0500 Subject: [PATCH 06/22] Small diff cleanups --- src/blocks/blockESLint.ts | 1 - src/blocks/blockREADME.test.ts | 2 + src/blocks/blockREADME.ts | 5 +- src/integration.test.ts | 14 -- .../createOptionDefaults/index.test.ts | 218 ------------------ 5 files changed, 5 insertions(+), 235 deletions(-) delete mode 100644 src/shared/options/createOptionDefaults/index.test.ts diff --git a/src/blocks/blockESLint.ts b/src/blocks/blockESLint.ts index 124b16407..942a26727 100644 --- a/src/blocks/blockESLint.ts +++ b/src/blocks/blockESLint.ts @@ -56,7 +56,6 @@ export const blockESLint = base.createBlock({ name: "ESLint", }, addons: { - allowDefaultProject: z.array(z.string()).default([]), beforeLint: z.string().optional(), explanations: z.array(z.string()).default([]), extensions: z.array(z.union([z.string(), zExtension])).default([]), diff --git a/src/blocks/blockREADME.test.ts b/src/blocks/blockREADME.test.ts index 4ac160622..0919821d9 100644 --- a/src/blocks/blockREADME.test.ts +++ b/src/blocks/blockREADME.test.ts @@ -116,6 +116,7 @@ describe("blockREADME", () => {

My logo + ## Usage Use it. @@ -160,6 +161,7 @@ describe("blockREADME", () => {

My logo + ## Usage Use it. diff --git a/src/blocks/blockREADME.ts b/src/blocks/blockREADME.ts index fc386be24..f7991b676 100644 --- a/src/blocks/blockREADME.ts +++ b/src/blocks/blockREADME.ts @@ -23,9 +23,10 @@ export const blockREADME = base.createBlock({ const logo = options.logo && - `\n`; + `\n\n`; - const explainer = options.explainer && `\n${options.explainer.join("\n")}`; + const explainer = + options.explainer && `\n${options.explainer.join("\n")}\n`; return { files: { diff --git a/src/integration.test.ts b/src/integration.test.ts index 6ee316fda..767b6b8ae 100644 --- a/src/integration.test.ts +++ b/src/integration.test.ts @@ -137,20 +137,6 @@ If you're interested in learning more, see the 'getting started' docs on: ", }, }, - ".vscode": { - "launch.json": "@@ -13,9 +13,9 @@ - }, - { - "name": "Debug Program", - "preLaunchTask": "build", - - "program": "./bin/index.js", - + "program": "bin/index.js", - "request": "launch", - "skipFiles": ["/**"], - "type": "node" - } - ", - }, "README.md": "@@ -1,9 +1,9 @@

Create TypeScript App

diff --git a/src/shared/options/createOptionDefaults/index.test.ts b/src/shared/options/createOptionDefaults/index.test.ts deleted file mode 100644 index efb36a8fc..000000000 --- a/src/shared/options/createOptionDefaults/index.test.ts +++ /dev/null @@ -1,218 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; - -import { createOptionDefaults } from "./index.js"; - -const mock$ = vi.fn(); - -vi.mock("execa", () => ({ - get $() { - return mock$; - }, -})); - -const mockGitUrlParse = vi.fn(); - -vi.mock("git-url-parse", () => ({ - get default() { - return mockGitUrlParse; - }, -})); - -const mockNpmUser = vi.fn(); - -vi.mock("npm-user", () => ({ - get default() { - return mockNpmUser; - }, -})); - -const mockReadPackageData = vi.fn(); - -vi.mock("../../packages.js", () => ({ - get readPackageData() { - return mockReadPackageData; - }, -})); - -const mockReadGitHubEmail = vi.fn(); - -vi.mock("./readGitHubEmail.js", () => ({ - get readGitHubEmail() { - return mockReadGitHubEmail; - }, -})); - -describe("createOptionDefaults", () => { - describe("bin", () => { - it("returns undefined when package data does not have a bin", async () => { - mockReadPackageData.mockResolvedValue({}); - - const actual = await createOptionDefaults().bin(); - - expect(actual).toBeUndefined(); - }); - - it("returns the bin when package data has a bin", async () => { - const bin = "lib/index.js"; - - mockReadPackageData.mockResolvedValue({ bin }); - - const actual = await createOptionDefaults().bin(); - - expect(actual).toBe(bin); - }); - }); - - describe("email", () => { - beforeEach(() => { - mockNpmUser.mockImplementation((username: string) => ({ - email: `npm-${username}@test.com`, - })); - }); - - it("returns the npm whoami email from npm when only an npm exists", async () => { - mock$.mockImplementation(([command]: string[]) => - command === "npm whoami" ? { stdout: "username" } : undefined, - ); - mockReadGitHubEmail.mockResolvedValueOnce(undefined); - - const actual = await createOptionDefaults().email(); - - expect(actual).toEqual({ - github: "npm-username@test.com", - npm: "npm-username@test.com", - }); - }); - - it("returns the npm whoami email from npm when only a package author email exists", async () => { - mock$.mockResolvedValue({ stdout: "" }); - mockReadGitHubEmail.mockResolvedValueOnce(undefined); - mockReadPackageData.mockResolvedValue({ - author: { - email: "test@package.com", - }, - }); - - const actual = await createOptionDefaults().email(); - - expect(actual).toEqual({ - github: "test@package.com", - npm: "test@package.com", - }); - }); - - it("returns the github email when only a github email exists", async () => { - mock$.mockResolvedValue({ stdout: "" }); - mockReadPackageData.mockResolvedValueOnce({}); - mockReadGitHubEmail.mockResolvedValueOnce("github@test.com"); - - const actual = await createOptionDefaults().email(); - - expect(actual).toEqual({ - github: "github@test.com", - npm: "github@test.com", - }); - }); - - it("returns the git user email when only a git user email exists", async () => { - mock$.mockImplementation(([command]: string[]) => - command === "git config --get user.email" - ? { stdout: "git@test.com" } - : undefined, - ); - mockReadGitHubEmail.mockResolvedValueOnce(undefined); - mockReadPackageData.mockResolvedValue({}); - - const actual = await createOptionDefaults().email(); - - expect(actual).toEqual({ - github: "git@test.com", - npm: "git@test.com", - }); - }); - - it("returns both the git user email and the npm user email when only those two exist", async () => { - mock$.mockImplementation(([command]: string[]) => ({ - stdout: - command === "git config --get user.email" - ? "git@test.com" - : "username", - })); - mockReadGitHubEmail.mockResolvedValueOnce(undefined); - mockReadPackageData.mockResolvedValue({}); - - const actual = await createOptionDefaults().email(); - - expect(actual).toEqual({ - github: "git@test.com", - npm: "npm-username@test.com", - }); - }); - - it("returns all three emails when they all exist", async () => { - mock$.mockImplementation(([command]: string[]) => ({ - stdout: - command === "git config --get user.email" - ? "git@test.com" - : "username", - })); - mockReadGitHubEmail.mockResolvedValueOnce("github@test.com"); - mockReadPackageData.mockResolvedValue({}); - - const actual = await createOptionDefaults().email(); - - expect(actual).toEqual({ - github: "github@test.com", - npm: "npm-username@test.com", - }); - }); - - it("returns undefined when none of the emails exist", async () => { - mock$.mockResolvedValue({ stdout: "" }); - mockReadGitHubEmail.mockResolvedValueOnce(undefined); - mockReadPackageData.mockResolvedValue({}); - - const actual = await createOptionDefaults().email(); - - expect(actual).toBeUndefined(); - }); - }); - - describe("repository", () => { - it("returns promptedOptions.repository when it exists", async () => { - const repository = "test-prompted-repository"; - const promptedOptions = { repository }; - const actual = await createOptionDefaults(promptedOptions).repository(); - - expect(actual).toBe(repository); - }); - - it("returns the Git name when it exists and promptedOptions.repository doesn't", async () => { - const name = "test-git-repository"; - mockGitUrlParse.mockResolvedValueOnce({ name }); - - const actual = await createOptionDefaults().repository(); - - expect(actual).toBe(name); - }); - - it("returns the package name when it exists and promptedOptions.repository and Git name don't", async () => { - const name = "test-package-name"; - mockReadPackageData.mockResolvedValueOnce({ name }); - - const actual = await createOptionDefaults().repository(); - - expect(actual).toBe(name); - }); - - it("returns the directory when it exists and promptedOptions.repository, Git name, and package name don't", async () => { - mockReadPackageData.mockResolvedValueOnce({}); - const directory = "test-prompted-directory"; - const promptedOptions = { directory }; - - const actual = await createOptionDefaults(promptedOptions).repository(); - - expect(actual).toBe(directory); - }); - }); -}); From c4625dd2858cc76a7cdbfffe442e1de9e417b6c5 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 15 Jan 2025 09:46:40 -0500 Subject: [PATCH 07/22] update integration snapshot for knip fix --- src/integration.test.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/integration.test.ts b/src/integration.test.ts index 767b6b8ae..d43624e45 100644 --- a/src/integration.test.ts +++ b/src/integration.test.ts @@ -176,16 +176,6 @@ If you're interested in learning more, see the 'getting started' docs on: import eslint from "@eslint/js"; import vitest from "@vitest/eslint-plugin"; import jsdoc from "eslint-plugin-jsdoc"; - ", - "knip.json": "@@ -1,7 +1,7 @@ - { - "$schema": "https://unpkg.com/knip@5.41.1/schema.json", - - "entry": ["src/index.ts", "src/**/*.test.*"], - + "entry": ["src/index.ts"], - "ignoreDependencies": ["all-contributors-cli", "cspell-populate-words"], - "ignoreExportsUsedInFile": { "interface": true, "type": true }, - "project": ["src/**/*.ts"] - } ", } `); From 04c3c749c14089377aa5cef29dbe109b19474d34 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 15 Jan 2025 09:47:32 -0500 Subject: [PATCH 08/22] update integration snapshot for eslint newline removal --- eslint.config.js | 1 - src/integration.test.ts | 11 ----------- 2 files changed, 12 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 758d20cbb..055d83f7e 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -7,7 +7,6 @@ If you're interested in learning more, see the 'getting started' docs on: - ESLint: https://eslint.org - typescript-eslint: https://typescript-eslint.io */ - import comments from "@eslint-community/eslint-plugin-eslint-comments/configs"; import eslint from "@eslint/js"; import vitest from "@vitest/eslint-plugin"; diff --git a/src/integration.test.ts b/src/integration.test.ts index d43624e45..7ceab3388 100644 --- a/src/integration.test.ts +++ b/src/integration.test.ts @@ -165,17 +165,6 @@ If you're interested in learning more, see the 'getting started' docs on: - - ", - "eslint.config.js": "@@ -6,9 +6,8 @@ - If you're interested in learning more, see the 'getting started' docs on: - - ESLint: https://eslint.org - - typescript-eslint: https://typescript-eslint.io - */ - - - import comments from "@eslint-community/eslint-plugin-eslint-comments/configs"; - import eslint from "@eslint/js"; - import vitest from "@vitest/eslint-plugin"; - import jsdoc from "eslint-plugin-jsdoc"; ", } `); From 82ed2c1e7385d7f7e951ad5c2a9df60702a6bb10 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 15 Jan 2025 09:51:08 -0500 Subject: [PATCH 09/22] Merge branch 'main' --- eslint.config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/eslint.config.js b/eslint.config.js index 055d83f7e..758d20cbb 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -7,6 +7,7 @@ If you're interested in learning more, see the 'getting started' docs on: - ESLint: https://eslint.org - typescript-eslint: https://typescript-eslint.io */ + import comments from "@eslint-community/eslint-plugin-eslint-comments/configs"; import eslint from "@eslint/js"; import vitest from "@vitest/eslint-plugin"; From 84227547b652726a7d3fa023eea15f40ad9f8b03 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 15 Jan 2025 09:56:00 -0500 Subject: [PATCH 10/22] fix: blockAllContributors table needs to also disable cspell --- README.md | 4 ++-- src/blocks/blockAllContributors.test.ts | 4 +++- src/blocks/blockAllContributors.ts | 2 ++ src/integration.test.ts | 29 ------------------------- 4 files changed, 7 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index e17c9bf57..97fc7ceea 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@

Create TypeScript App

- Quickstart-friendly TypeScript template with comprehensive, configurable, opinionated tooling. - 🎁 + Quickstart-friendly TypeScript template with comprehensive, configurable, opinionated tooling. + 🎁

diff --git a/src/blocks/blockAllContributors.test.ts b/src/blocks/blockAllContributors.test.ts index 59151d897..616330758 100644 --- a/src/blocks/blockAllContributors.test.ts +++ b/src/blocks/blockAllContributors.test.ts @@ -116,6 +116,7 @@ describe("blockAllContributors", () => { "sections": [ "## Contributors + @@ -130,7 +131,8 @@ describe("blockAllContributors", () => { - ", + + ", ], }, "block": [Function], diff --git a/src/blocks/blockAllContributors.ts b/src/blocks/blockAllContributors.ts index cc8f02013..6a996940a 100644 --- a/src/blocks/blockAllContributors.ts +++ b/src/blocks/blockAllContributors.ts @@ -81,6 +81,7 @@ function printAllContributorsTable(contributors: Contributor[]) { return [ `## Contributors`, ``, + ``, ``, ``, ``, @@ -102,6 +103,7 @@ function printAllContributorsTable(contributors: Contributor[]) { ``, ``, ``, + ``, ].join("\n"); } diff --git a/src/integration.test.ts b/src/integration.test.ts index 7ceab3388..2845937fb 100644 --- a/src/integration.test.ts +++ b/src/integration.test.ts @@ -137,35 +137,6 @@ If you're interested in learning more, see the 'getting started' docs on: ", }, }, - "README.md": "@@ -1,9 +1,9 @@ -

Create TypeScript App

- -

- - Quickstart-friendly TypeScript template with comprehensive, configurable, opinionated tooling. - - 🎁 - + Quickstart-friendly TypeScript template with comprehensive, configurable, opinionated tooling. - + 🎁 -

- -

- - @@ -64,9 +64,8 @@ - Thanks! 💖 - - ## Contributors - - - - - - - - @@ -145,5 +144,4 @@ - - - - - - - ", } `); }); From 158010b04ae5dafa418c5b0e4709ae7f7ebab23e Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 15 Jan 2025 09:59:53 -0500 Subject: [PATCH 11/22] add CODECOV_TOKEN to integration test --- create.config.ts | 56 ----------------------------------------- src/blocks/index.ts | 3 +++ src/integration.test.ts | 20 +++++---------- 3 files changed, 9 insertions(+), 70 deletions(-) delete mode 100644 create.config.ts diff --git a/create.config.ts b/create.config.ts deleted file mode 100644 index 01bb5e71c..000000000 --- a/create.config.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { createConfig } from "create"; - -import { - blockAreTheTypesWrong, - blockESLint, - blockTemplatedWith, - blockTSup, - presets, -} from "./src/index.ts"; - -export default createConfig(presets.everything, { - addons: [ - blockESLint({ - explanations: [ - `👋 Hi! This ESLint configuration contains a lot more stuff than many repos'! -You can read from it to see all sorts of linting goodness, but don't worry - -it's not something you need to exhaustively understand immediately. 💙 - -If you're interested in learning more, see the 'getting started' docs on: -- ESLint: https://eslint.org -- typescript-eslint: https://typescript-eslint.io`, - ], - rules: [ - { - comment: - "These on-by-default rules work well for this repo if configured", - entries: { - "@typescript-eslint/no-unnecessary-condition": [ - "error", - { allowConstantLoopConditions: true }, - ], - "@typescript-eslint/prefer-nullish-coalescing": [ - "error", - { ignorePrimitives: true }, - ], - "@typescript-eslint/restrict-template-expressions": [ - "error", - { allowBoolean: true, allowNullish: true, allowNumber: true }, - ], - "n/no-unsupported-features/node-builtins": [ - "error", - { allowExperimental: true }, - ], - }, - }, - ], - }), - blockTSup({ - runArgs: ["--version"], - }), - ], - blocks: { - add: [blockAreTheTypesWrong], - exclude: [blockTemplatedWith], - }, -}); diff --git a/src/blocks/index.ts b/src/blocks/index.ts index efc0fb385..300b4cf0a 100644 --- a/src/blocks/index.ts +++ b/src/blocks/index.ts @@ -1,5 +1,6 @@ import { blockAllContributors } from "./blockAllContributors.js"; import { blockAreTheTypesWrong } from "./blockAreTheTypesWrong.js"; +import { blockCodecov } from "./blockCodecov.js"; import { blockContributingDocs } from "./blockContributingDocs.js"; import { blockContributorCovenant } from "./blockContributorCovenant.js"; import { blockCSpell } from "./blockCSpell.js"; @@ -44,6 +45,7 @@ import { blockVSCode } from "./blockVSCode.js"; export const blocks = { blockAllContributors, blockAreTheTypesWrong, + blockCodecov, blockContributingDocs, blockContributorCovenant, blockCSpell, @@ -88,6 +90,7 @@ export const blocks = { export { blockAllContributors } from "./blockAllContributors.js"; export { blockAreTheTypesWrong } from "./blockAreTheTypesWrong.js"; +export { blockCodecov } from "./blockCodecov.js"; export { blockContributingDocs } from "./blockContributingDocs.js"; export { blockContributorCovenant } from "./blockContributorCovenant.js"; export { blockCSpell } from "./blockCSpell.js"; diff --git a/src/integration.test.ts b/src/integration.test.ts index 2845937fb..1ee3b31b7 100644 --- a/src/integration.test.ts +++ b/src/integration.test.ts @@ -8,6 +8,7 @@ import { base, BaseOptions, blockAreTheTypesWrong, + blockCodecov, blockCSpell, blockESLint, blockKnip, @@ -39,6 +40,11 @@ test("Producing the everything preset matches the files in this repository", asy const created = await producePreset(presets.everything, { addons: [ + blockCodecov({ + env: { + CODECOV_TOKEN: "${{ secrets.CODECOV_TOKEN }}", + }, + }), blockCSpell({ words: [ "Anson", @@ -105,20 +111,6 @@ If you're interested in learning more, see the 'getting started' docs on: { ".github": { "workflows": { - "ci.yml": "@@ -64,11 +64,9 @@ - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/prepare - - run: pnpm run test --coverage - - - env: - - CODECOV_TOKEN: \${{ secrets.CODECOV_TOKEN }} - - if: always() - + - if: always() - uses: codecov/codecov-action@v3 - type_check: - name: Type Check - runs-on: ubuntu-latest - ", "release.yml": "@@ -14,13 +14,9 @@ - run: pnpm build - env: From c3af52124bb2062fd30520e0d239dcb72b61d388 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 15 Jan 2025 10:05:36 -0500 Subject: [PATCH 12/22] docs: add missing Codecov to Blocks.md --- docs/Blocks.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Blocks.md b/docs/Blocks.md index 6ff111b38..ccb908ffe 100644 --- a/docs/Blocks.md +++ b/docs/Blocks.md @@ -12,6 +12,7 @@ This table summarizes each block and which base levels they're included in: | Contributing Docs | `--exclude-contributing-docs` | ✔️ | ✅ | 💯 | | Contributor Covenant | `--exclude-contributor-covenant` | ✔️ | ✅ | 💯 | | CSpell | `--exclude-cspell` | | | 💯 | +| Codecov | `--exclude-codecov` | | ✅ | 💯 | | Development Docs | `--exclude-development-docs` | ✔️ | ✅ | 💯 | | ESLint | `--exclude-eslint` | ✔️ | ✅ | 💯 | | ESLint Comments Plugin | `--exclude-eslint-comments-plugin` | | | 💯 | From 1fcff5fda4f6336dd17aa5c9a5fe83acd5f9edf2 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 15 Jan 2025 10:18:56 -0500 Subject: [PATCH 13/22] bump get-github-auth-token to avoid crash --- pnpm-lock.yaml | 210 ++++++++++++++++++++++++------------------------- 1 file changed, 105 insertions(+), 105 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2487195e8..feefb85ff 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -509,8 +509,8 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.23.0': - resolution: {integrity: sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ==} + '@esbuild/aix-ppc64@0.23.1': + resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] @@ -527,8 +527,8 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.23.0': - resolution: {integrity: sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ==} + '@esbuild/android-arm64@0.23.1': + resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} engines: {node: '>=18'} cpu: [arm64] os: [android] @@ -545,8 +545,8 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.23.0': - resolution: {integrity: sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g==} + '@esbuild/android-arm@0.23.1': + resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} engines: {node: '>=18'} cpu: [arm] os: [android] @@ -563,8 +563,8 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.23.0': - resolution: {integrity: sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ==} + '@esbuild/android-x64@0.23.1': + resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} engines: {node: '>=18'} cpu: [x64] os: [android] @@ -581,8 +581,8 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.23.0': - resolution: {integrity: sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow==} + '@esbuild/darwin-arm64@0.23.1': + resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] @@ -599,8 +599,8 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.23.0': - resolution: {integrity: sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ==} + '@esbuild/darwin-x64@0.23.1': + resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} engines: {node: '>=18'} cpu: [x64] os: [darwin] @@ -617,8 +617,8 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.23.0': - resolution: {integrity: sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw==} + '@esbuild/freebsd-arm64@0.23.1': + resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] @@ -635,8 +635,8 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.23.0': - resolution: {integrity: sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ==} + '@esbuild/freebsd-x64@0.23.1': + resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] @@ -653,8 +653,8 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.23.0': - resolution: {integrity: sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw==} + '@esbuild/linux-arm64@0.23.1': + resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} engines: {node: '>=18'} cpu: [arm64] os: [linux] @@ -671,8 +671,8 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.23.0': - resolution: {integrity: sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw==} + '@esbuild/linux-arm@0.23.1': + resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} engines: {node: '>=18'} cpu: [arm] os: [linux] @@ -689,8 +689,8 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.23.0': - resolution: {integrity: sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA==} + '@esbuild/linux-ia32@0.23.1': + resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} engines: {node: '>=18'} cpu: [ia32] os: [linux] @@ -707,8 +707,8 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.23.0': - resolution: {integrity: sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A==} + '@esbuild/linux-loong64@0.23.1': + resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} engines: {node: '>=18'} cpu: [loong64] os: [linux] @@ -725,8 +725,8 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.23.0': - resolution: {integrity: sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w==} + '@esbuild/linux-mips64el@0.23.1': + resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] @@ -743,8 +743,8 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.23.0': - resolution: {integrity: sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw==} + '@esbuild/linux-ppc64@0.23.1': + resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] @@ -761,8 +761,8 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.23.0': - resolution: {integrity: sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw==} + '@esbuild/linux-riscv64@0.23.1': + resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] @@ -779,8 +779,8 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.23.0': - resolution: {integrity: sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg==} + '@esbuild/linux-s390x@0.23.1': + resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] @@ -797,8 +797,8 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.23.0': - resolution: {integrity: sha512-a3pMQhUEJkITgAw6e0bWA+F+vFtCciMjW/LPtoj99MhVt+Mfb6bbL9hu2wmTZgNd994qTAEw+U/r6k3qHWWaOQ==} + '@esbuild/linux-x64@0.23.1': + resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} engines: {node: '>=18'} cpu: [x64] os: [linux] @@ -815,8 +815,8 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.23.0': - resolution: {integrity: sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw==} + '@esbuild/netbsd-x64@0.23.1': + resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] @@ -827,8 +827,8 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.23.0': - resolution: {integrity: sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ==} + '@esbuild/openbsd-arm64@0.23.1': + resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] @@ -845,8 +845,8 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.23.0': - resolution: {integrity: sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg==} + '@esbuild/openbsd-x64@0.23.1': + resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] @@ -863,8 +863,8 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.23.0': - resolution: {integrity: sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA==} + '@esbuild/sunos-x64@0.23.1': + resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] @@ -881,8 +881,8 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.23.0': - resolution: {integrity: sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==} + '@esbuild/win32-arm64@0.23.1': + resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} engines: {node: '>=18'} cpu: [arm64] os: [win32] @@ -899,8 +899,8 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.23.0': - resolution: {integrity: sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==} + '@esbuild/win32-ia32@0.23.1': + resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} engines: {node: '>=18'} cpu: [ia32] os: [win32] @@ -917,8 +917,8 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.23.0': - resolution: {integrity: sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g==} + '@esbuild/win32-x64@0.23.1': + resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -2148,8 +2148,8 @@ packages: engines: {node: '>=12'} hasBin: true - esbuild@0.23.0: - resolution: {integrity: sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==} + esbuild@0.23.1: + resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} engines: {node: '>=18'} hasBin: true @@ -2447,8 +2447,8 @@ packages: resolution: {integrity: sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==} engines: {node: '>=18'} - get-github-auth-token@0.1.0: - resolution: {integrity: sha512-ENm+A39AV0X4+Ls1jiCvmqx+C8hSYTv4d5hV9Ks+EL+gx9a0P9pYYpRE1k2ExwRT3EFQabGXF1Rkdp5FIsLkiw==} + get-github-auth-token@0.1.1: + resolution: {integrity: sha512-SifKYtFehQUVgeEBiTeQMGWeNfNx59gZ7GzYzD6vKCIGDh5YT+Axm5i0VgP0XaD8TeN8ABtp593eX+wSFk8IpQ==} engines: {node: '>=18'} get-stdin@9.0.0: @@ -4683,7 +4683,7 @@ snapshots: '@esbuild/aix-ppc64@0.19.12': optional: true - '@esbuild/aix-ppc64@0.23.0': + '@esbuild/aix-ppc64@0.23.1': optional: true '@esbuild/aix-ppc64@0.24.0': @@ -4692,7 +4692,7 @@ snapshots: '@esbuild/android-arm64@0.19.12': optional: true - '@esbuild/android-arm64@0.23.0': + '@esbuild/android-arm64@0.23.1': optional: true '@esbuild/android-arm64@0.24.0': @@ -4701,7 +4701,7 @@ snapshots: '@esbuild/android-arm@0.19.12': optional: true - '@esbuild/android-arm@0.23.0': + '@esbuild/android-arm@0.23.1': optional: true '@esbuild/android-arm@0.24.0': @@ -4710,7 +4710,7 @@ snapshots: '@esbuild/android-x64@0.19.12': optional: true - '@esbuild/android-x64@0.23.0': + '@esbuild/android-x64@0.23.1': optional: true '@esbuild/android-x64@0.24.0': @@ -4719,7 +4719,7 @@ snapshots: '@esbuild/darwin-arm64@0.19.12': optional: true - '@esbuild/darwin-arm64@0.23.0': + '@esbuild/darwin-arm64@0.23.1': optional: true '@esbuild/darwin-arm64@0.24.0': @@ -4728,7 +4728,7 @@ snapshots: '@esbuild/darwin-x64@0.19.12': optional: true - '@esbuild/darwin-x64@0.23.0': + '@esbuild/darwin-x64@0.23.1': optional: true '@esbuild/darwin-x64@0.24.0': @@ -4737,7 +4737,7 @@ snapshots: '@esbuild/freebsd-arm64@0.19.12': optional: true - '@esbuild/freebsd-arm64@0.23.0': + '@esbuild/freebsd-arm64@0.23.1': optional: true '@esbuild/freebsd-arm64@0.24.0': @@ -4746,7 +4746,7 @@ snapshots: '@esbuild/freebsd-x64@0.19.12': optional: true - '@esbuild/freebsd-x64@0.23.0': + '@esbuild/freebsd-x64@0.23.1': optional: true '@esbuild/freebsd-x64@0.24.0': @@ -4755,7 +4755,7 @@ snapshots: '@esbuild/linux-arm64@0.19.12': optional: true - '@esbuild/linux-arm64@0.23.0': + '@esbuild/linux-arm64@0.23.1': optional: true '@esbuild/linux-arm64@0.24.0': @@ -4764,7 +4764,7 @@ snapshots: '@esbuild/linux-arm@0.19.12': optional: true - '@esbuild/linux-arm@0.23.0': + '@esbuild/linux-arm@0.23.1': optional: true '@esbuild/linux-arm@0.24.0': @@ -4773,7 +4773,7 @@ snapshots: '@esbuild/linux-ia32@0.19.12': optional: true - '@esbuild/linux-ia32@0.23.0': + '@esbuild/linux-ia32@0.23.1': optional: true '@esbuild/linux-ia32@0.24.0': @@ -4782,7 +4782,7 @@ snapshots: '@esbuild/linux-loong64@0.19.12': optional: true - '@esbuild/linux-loong64@0.23.0': + '@esbuild/linux-loong64@0.23.1': optional: true '@esbuild/linux-loong64@0.24.0': @@ -4791,7 +4791,7 @@ snapshots: '@esbuild/linux-mips64el@0.19.12': optional: true - '@esbuild/linux-mips64el@0.23.0': + '@esbuild/linux-mips64el@0.23.1': optional: true '@esbuild/linux-mips64el@0.24.0': @@ -4800,7 +4800,7 @@ snapshots: '@esbuild/linux-ppc64@0.19.12': optional: true - '@esbuild/linux-ppc64@0.23.0': + '@esbuild/linux-ppc64@0.23.1': optional: true '@esbuild/linux-ppc64@0.24.0': @@ -4809,7 +4809,7 @@ snapshots: '@esbuild/linux-riscv64@0.19.12': optional: true - '@esbuild/linux-riscv64@0.23.0': + '@esbuild/linux-riscv64@0.23.1': optional: true '@esbuild/linux-riscv64@0.24.0': @@ -4818,7 +4818,7 @@ snapshots: '@esbuild/linux-s390x@0.19.12': optional: true - '@esbuild/linux-s390x@0.23.0': + '@esbuild/linux-s390x@0.23.1': optional: true '@esbuild/linux-s390x@0.24.0': @@ -4827,7 +4827,7 @@ snapshots: '@esbuild/linux-x64@0.19.12': optional: true - '@esbuild/linux-x64@0.23.0': + '@esbuild/linux-x64@0.23.1': optional: true '@esbuild/linux-x64@0.24.0': @@ -4836,13 +4836,13 @@ snapshots: '@esbuild/netbsd-x64@0.19.12': optional: true - '@esbuild/netbsd-x64@0.23.0': + '@esbuild/netbsd-x64@0.23.1': optional: true '@esbuild/netbsd-x64@0.24.0': optional: true - '@esbuild/openbsd-arm64@0.23.0': + '@esbuild/openbsd-arm64@0.23.1': optional: true '@esbuild/openbsd-arm64@0.24.0': @@ -4851,7 +4851,7 @@ snapshots: '@esbuild/openbsd-x64@0.19.12': optional: true - '@esbuild/openbsd-x64@0.23.0': + '@esbuild/openbsd-x64@0.23.1': optional: true '@esbuild/openbsd-x64@0.24.0': @@ -4860,7 +4860,7 @@ snapshots: '@esbuild/sunos-x64@0.19.12': optional: true - '@esbuild/sunos-x64@0.23.0': + '@esbuild/sunos-x64@0.23.1': optional: true '@esbuild/sunos-x64@0.24.0': @@ -4869,7 +4869,7 @@ snapshots: '@esbuild/win32-arm64@0.19.12': optional: true - '@esbuild/win32-arm64@0.23.0': + '@esbuild/win32-arm64@0.23.1': optional: true '@esbuild/win32-arm64@0.24.0': @@ -4878,7 +4878,7 @@ snapshots: '@esbuild/win32-ia32@0.19.12': optional: true - '@esbuild/win32-ia32@0.23.0': + '@esbuild/win32-ia32@0.23.1': optional: true '@esbuild/win32-ia32@0.24.0': @@ -4887,7 +4887,7 @@ snapshots: '@esbuild/win32-x64@0.19.12': optional: true - '@esbuild/win32-x64@0.23.0': + '@esbuild/win32-x64@0.23.1': optional: true '@esbuild/win32-x64@0.24.0': @@ -5952,7 +5952,7 @@ snapshots: chalk: 5.4.1 create-fs: 0.1.0 execa: 9.5.2 - get-github-auth-token: 0.1.0 + get-github-auth-token: 0.1.1 hash-object: 5.0.1 hosted-git-info: 8.0.2 import-local-or-npx: 0.2.0 @@ -6217,32 +6217,32 @@ snapshots: '@esbuild/win32-ia32': 0.19.12 '@esbuild/win32-x64': 0.19.12 - esbuild@0.23.0: + esbuild@0.23.1: optionalDependencies: - '@esbuild/aix-ppc64': 0.23.0 - '@esbuild/android-arm': 0.23.0 - '@esbuild/android-arm64': 0.23.0 - '@esbuild/android-x64': 0.23.0 - '@esbuild/darwin-arm64': 0.23.0 - '@esbuild/darwin-x64': 0.23.0 - '@esbuild/freebsd-arm64': 0.23.0 - '@esbuild/freebsd-x64': 0.23.0 - '@esbuild/linux-arm': 0.23.0 - '@esbuild/linux-arm64': 0.23.0 - '@esbuild/linux-ia32': 0.23.0 - '@esbuild/linux-loong64': 0.23.0 - '@esbuild/linux-mips64el': 0.23.0 - '@esbuild/linux-ppc64': 0.23.0 - '@esbuild/linux-riscv64': 0.23.0 - '@esbuild/linux-s390x': 0.23.0 - '@esbuild/linux-x64': 0.23.0 - '@esbuild/netbsd-x64': 0.23.0 - '@esbuild/openbsd-arm64': 0.23.0 - '@esbuild/openbsd-x64': 0.23.0 - '@esbuild/sunos-x64': 0.23.0 - '@esbuild/win32-arm64': 0.23.0 - '@esbuild/win32-ia32': 0.23.0 - '@esbuild/win32-x64': 0.23.0 + '@esbuild/aix-ppc64': 0.23.1 + '@esbuild/android-arm': 0.23.1 + '@esbuild/android-arm64': 0.23.1 + '@esbuild/android-x64': 0.23.1 + '@esbuild/darwin-arm64': 0.23.1 + '@esbuild/darwin-x64': 0.23.1 + '@esbuild/freebsd-arm64': 0.23.1 + '@esbuild/freebsd-x64': 0.23.1 + '@esbuild/linux-arm': 0.23.1 + '@esbuild/linux-arm64': 0.23.1 + '@esbuild/linux-ia32': 0.23.1 + '@esbuild/linux-loong64': 0.23.1 + '@esbuild/linux-mips64el': 0.23.1 + '@esbuild/linux-ppc64': 0.23.1 + '@esbuild/linux-riscv64': 0.23.1 + '@esbuild/linux-s390x': 0.23.1 + '@esbuild/linux-x64': 0.23.1 + '@esbuild/netbsd-x64': 0.23.1 + '@esbuild/openbsd-arm64': 0.23.1 + '@esbuild/openbsd-x64': 0.23.1 + '@esbuild/sunos-x64': 0.23.1 + '@esbuild/win32-arm64': 0.23.1 + '@esbuild/win32-ia32': 0.23.1 + '@esbuild/win32-x64': 0.23.1 optional: true esbuild@0.24.0: @@ -6653,7 +6653,7 @@ snapshots: get-east-asian-width@1.2.0: {} - get-github-auth-token@0.1.0: {} + get-github-auth-token@0.1.1: {} get-stdin@9.0.0: {} @@ -7637,7 +7637,7 @@ snapshots: octokit-from-auth@0.2.0: dependencies: - get-github-auth-token: 0.1.0 + get-github-auth-token: 0.1.1 octokit: 4.1.0 octokit@4.1.0: @@ -8447,7 +8447,7 @@ snapshots: tsx@4.19.2: dependencies: - esbuild: 0.23.0 + esbuild: 0.23.1 get-tsconfig: 4.8.1 optionalDependencies: fsevents: 2.3.3 From 5f5676a674910dd7eba472a1f3b9be1519aa1a06 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 15 Jan 2025 11:28:56 -0500 Subject: [PATCH 14/22] Switch pre-commit file mode: ... to executable: true --- package.json | 6 ++-- pnpm-lock.yaml | 58 ++++++++++++++++---------------- src/blocks/blockPrettier.test.ts | 6 ++-- src/blocks/blockPrettier.ts | 2 +- 4 files changed, 36 insertions(+), 36 deletions(-) diff --git a/package.json b/package.json index 61d05426c..28aaf7772 100644 --- a/package.json +++ b/package.json @@ -38,8 +38,8 @@ }, "dependencies": { "chalk": "^5.4.1", - "create": "0.1.0-alpha.13", - "create-fs": "^0.1.0", + "create": "0.1.0-alpha.14", + "create-fs": "^0.1.1", "cspell-populate-words": "^0.3.0", "execa": "^9.5.2", "git-remote-origin-url": "^4.0.0", @@ -78,7 +78,7 @@ "@vitest/eslint-plugin": "1.1.20", "all-contributors-cli": "6.26.1", "console-fail-test": "0.5.0", - "create-testers": "0.1.0-alpha.13", + "create-testers": "0.1.0-alpha.14", "cspell": "^8.17.2", "eslint": "9.17.0", "eslint-plugin-jsdoc": "50.6.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index feefb85ff..dabe8f3ee 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,11 +12,11 @@ importers: specifier: ^5.4.1 version: 5.4.1 create: - specifier: 0.1.0-alpha.13 - version: 0.1.0-alpha.13 + specifier: 0.1.0-alpha.14 + version: 0.1.0-alpha.14 create-fs: - specifier: ^0.1.0 - version: 0.1.0 + specifier: ^0.1.1 + version: 0.1.1 cspell-populate-words: specifier: ^0.3.0 version: 0.3.0 @@ -37,13 +37,13 @@ importers: version: 1.2.0 input-from-file: specifier: 0.1.0-alpha.4 - version: 0.1.0-alpha.4(create@0.1.0-alpha.13) + version: 0.1.0-alpha.4(create@0.1.0-alpha.14) input-from-file-json: specifier: 0.1.0-alpha.4 - version: 0.1.0-alpha.4(create@0.1.0-alpha.13) + version: 0.1.0-alpha.4(create@0.1.0-alpha.14) input-from-script: specifier: 0.1.0-alpha.4 - version: 0.1.0-alpha.4(create@0.1.0-alpha.13) + version: 0.1.0-alpha.4(create@0.1.0-alpha.14) js-yaml: specifier: ^4.1.0 version: 4.1.0 @@ -127,8 +127,8 @@ importers: specifier: 0.5.0 version: 0.5.0 create-testers: - specifier: 0.1.0-alpha.13 - version: 0.1.0-alpha.13(create-fs@0.1.0)(create@0.1.0-alpha.13) + specifier: 0.1.0-alpha.14 + version: 0.1.0-alpha.14(create-fs@0.1.1)(create@0.1.0-alpha.14) cspell: specifier: ^8.17.2 version: 8.17.2 @@ -1919,19 +1919,19 @@ packages: typescript: optional: true - create-fs@0.1.0: - resolution: {integrity: sha512-IxoO6aFIPs+loAGrBn/Hg8UzQ5pRfjtsNqSW8o/RMtjh0RShZcuKbDYWvepnjTqDAaHdbkaXFY7y86WXqQnzpA==} + create-fs@0.1.1: + resolution: {integrity: sha512-tZqeDZ7yLLje2meTDL1pR3oBAnSlDd8fNyRUHtPxhGIGUGEoY8A/DHIpfxHE9eEoamA2snDJlVP9HyItfb18gQ==} engines: {node: '>=18'} - create-testers@0.1.0-alpha.13: - resolution: {integrity: sha512-rrCLIajWABQcakAr9eLwIqmzaXVx8zyfE/dXKf2Xj/L4+7m/48JE+4q8gRLh6SYsb4c/SFlaKZ/q50gIWitDcg==} + create-testers@0.1.0-alpha.14: + resolution: {integrity: sha512-IWK69nhyVEhMTDfmOwAr3Hlj3jtQ8P3mz4ezo77BI0er82KpPaNGuSePbj5N9NWfP5TuJeeFJ+uEDcHb2x7fzA==} engines: {node: '>=18'} peerDependencies: - create: ^0.1.0-alpha.13 + create: ^0.1.0-alpha.14 create-fs: ^0.1.0 - create@0.1.0-alpha.13: - resolution: {integrity: sha512-8pX3fXtYzyXcCkMppv7R9hsw0SFbxQxzkEDsjX8WyIQd0jPuSIQMr0ofZEUthJQG63NlJCvXmS2V4H7LQDUpFA==} + create@0.1.0-alpha.14: + resolution: {integrity: sha512-OExXy9Tn7Nugayz/eKMJV1/oM7J/p2SOi3HK/zEyMXNwF2IwnPiHli6rjyvl5hggU7618cumdD6D5ptJK1Zixw==} engines: {node: '>=18'} hasBin: true @@ -5935,22 +5935,22 @@ snapshots: optionalDependencies: typescript: 5.7.2 - create-fs@0.1.0: {} + create-fs@0.1.1: {} - create-testers@0.1.0-alpha.13(create-fs@0.1.0)(create@0.1.0-alpha.13): + create-testers@0.1.0-alpha.14(create-fs@0.1.1)(create@0.1.0-alpha.14): dependencies: - create: 0.1.0-alpha.13 - create-fs: 0.1.0 + create: 0.1.0-alpha.14 + create-fs: 0.1.1 diff: 7.0.0 octokit: 4.1.0 without-undefined-properties: 0.1.1 - create@0.1.0-alpha.13: + create@0.1.0-alpha.14: dependencies: '@clack/prompts': 0.9.1 cached-factory: 0.1.0 chalk: 5.4.1 - create-fs: 0.1.0 + create-fs: 0.1.1 execa: 9.5.2 get-github-auth-token: 0.1.1 hash-object: 5.0.1 @@ -6901,20 +6901,20 @@ snapshots: ini@4.1.1: {} - input-from-file-json@0.1.0-alpha.4(create@0.1.0-alpha.13): + input-from-file-json@0.1.0-alpha.4(create@0.1.0-alpha.14): dependencies: - create: 0.1.0-alpha.13 - input-from-file: 0.1.0-alpha.4(create@0.1.0-alpha.13) + create: 0.1.0-alpha.14 + input-from-file: 0.1.0-alpha.4(create@0.1.0-alpha.14) zod: 3.24.1 - input-from-file@0.1.0-alpha.4(create@0.1.0-alpha.13): + input-from-file@0.1.0-alpha.4(create@0.1.0-alpha.14): dependencies: - create: 0.1.0-alpha.13 + create: 0.1.0-alpha.14 zod: 3.24.1 - input-from-script@0.1.0-alpha.4(create@0.1.0-alpha.13): + input-from-script@0.1.0-alpha.4(create@0.1.0-alpha.14): dependencies: - create: 0.1.0-alpha.13 + create: 0.1.0-alpha.14 zod: 3.24.1 inquirer@7.3.3: diff --git a/src/blocks/blockPrettier.test.ts b/src/blocks/blockPrettier.test.ts index 3bdad764b..7c36aa44a 100644 --- a/src/blocks/blockPrettier.test.ts +++ b/src/blocks/blockPrettier.test.ts @@ -94,7 +94,7 @@ describe("blockPrettier", () => { "npx lint-staged ", { - "mode": 33279, + "executable": true, }, ], }, @@ -206,7 +206,7 @@ describe("blockPrettier", () => { "npx lint-staged ", { - "mode": 33279, + "executable": true, }, ], }, @@ -335,7 +335,7 @@ describe("blockPrettier", () => { "npx lint-staged ", { - "mode": 33279, + "executable": true, }, ], }, diff --git a/src/blocks/blockPrettier.ts b/src/blocks/blockPrettier.ts index ce2d7147a..d54671820 100644 --- a/src/blocks/blockPrettier.ts +++ b/src/blocks/blockPrettier.ts @@ -95,7 +95,7 @@ pnpm format --write files: { ".husky": { ".gitignore": "_\n", - "pre-commit": ["npx lint-staged\n", { mode: 33279 }], + "pre-commit": ["npx lint-staged\n", { executable: true }], }, ".prettierignore": formatIgnoreFile( ["/.husky", "/lib", "/pnpm-lock.yaml", ...ignores].sort(), From 3717ae87684ee9f3df6276e05604c6dda2e8637c Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 15 Jan 2025 14:24:41 -0500 Subject: [PATCH 15/22] Merge branch 'main' --- src/base.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/base.test.ts b/src/base.test.ts index f69445dfa..9734102e7 100644 --- a/src/base.test.ts +++ b/src/base.test.ts @@ -27,7 +27,7 @@ describe("base", () => { }, explainer: [ `\`create-typescript-app\` is a one-stop-shop solution to set up a new or existing repository with the latest and greatest TypeScript tooling.`, - `It includes options not just for building and testing but also GitHub repository templates, contributor recognition, automated release management, and more.`, + `It includes options not just for building and testing but also automated release management, contributor recognition, GitHub repository settings, and more.`, ], funding: "JoshuaKGoldberg", guide: { From a68b0c5ae8c10a556d90134d2c529c2969d5cf1e Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 15 Jan 2025 14:38:11 -0500 Subject: [PATCH 16/22] Assorted docs touchups --- README.md | 2 +- docs/Blocks.md | 8 ++++---- docs/Creation.md | 12 ++++++------ docs/Initialization.md | 10 ++++------ docs/Migration.md | 8 ++++---- src/presets/common.ts | 2 ++ src/presets/everything.ts | 2 -- src/presets/minimal.ts | 4 ++-- 8 files changed, 23 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 006a54bb6..cb7690d91 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ You can read more about the supported setup modes in their docs pages: You can read more about `create-typescript-app` and the tooling it supports: -1. [**Tooling**](./docs/Tooling.md): a breakdown of all the pieces this template can set up. +1. [**Blocks**](./docs/Blocks.md): a breakdown of all the pieces this template can set up. 2. [**Options**](./docs/Options.md): granular options to customize how the template is run. 3. [**FAQs**](./docs/FAQs.md): frequently asked questions diff --git a/docs/Blocks.md b/docs/Blocks.md index ccb908ffe..2b828d9a7 100644 --- a/docs/Blocks.md +++ b/docs/Blocks.md @@ -1,6 +1,6 @@ # Blocks -`create-typescript-app` provides over two dozen pieces of tooling, ranging from code building and formatting to various forms of GitHub repository management. +`create-typescript-app` provides several dozen pieces of tooling, ranging from code building and formatting to various forms of GitHub repository management. Each can be individually turned off or on. This table summarizes each block and which base levels they're included in: @@ -25,10 +25,10 @@ This table summarizes each block and which base levels they're included in: | ESLint Perfectionist Plugin | `--exclude-eslint-perfectionist-plugin` | | | 💯 | | ESLint Regexp Plugin | `--exclude-eslint-regexp-plugin` | | | 💯 | | ESLint YML Plugin | `--exclude-eslint-yml-plugin` | | | 💯 | -| Funding | `--exclude-funding` | ✔️ | ✅ | 💯 | +| Funding | `--exclude-funding` | | ✅ | 💯 | | GitHub Actions CI | `--exclude-github-actions-ci` | ✔️ | ✅ | 💯 | | GitHub Issue Templates | `--exclude-github-issue-templates` | ✔️ | ✅ | 💯 | -| GitHub Issue Templates | `--exclude-github-issue-templates` | ✔️ | ✅ | 💯 | +| GitHub PR Template | `--exclude-github-pr-template` | ✔️ | ✅ | 💯 | | Gitignore | `--exclude-gitignore` | ✔️ | ✅ | 💯 | | Knip | `--exclude-knip` | | | 💯 | | Markdownlint | `--exclude-markdownlint` | | | 💯 | @@ -44,7 +44,7 @@ This table summarizes each block and which base levels they're included in: | README.md | `--exclude-readme-md` | ✔️ | ✅ | 💯 | | release-it | `--exclude-release-it` | | ✅ | 💯 | | Renovate | `--exclude-renovate` | | | 💯 | -| Security Docs | `--exclude-security-docs` | | | 💯 | +| Security Docs | `--exclude-security-docs` | ✔️ | ✅ | 💯 | | Templated By Notice | `--exclude-templated-by-notice` | ✔️ | ✅ | 💯 | | TSup | `--exclude-tsup` | ✔️ | ✅ | 💯 | | TypeScript | `--exclude-typescript` | ✔️ | ✅ | 💯 | diff --git a/docs/Creation.md b/docs/Creation.md index ba1f3ac04..1fa7bfd06 100644 --- a/docs/Creation.md +++ b/docs/Creation.md @@ -1,6 +1,6 @@ # Creating from the Terminal -You can run `npx create typescript-app` in your terminal to interactively create a new repository in a child directory: +You can run `npx create typescript-app` in your terminal to interactively create a new repository: ```shell npx create typescript-app @@ -8,8 +8,8 @@ npx create typescript-app The creation script will by default: -1. Create a new directory with the given repository name -2. Initialize that new directory as a local Git repository +1. Prompt you for a directory, which template preset to run with, and some starting information +2. Initialize new directory as a local Git repository 3. Copy the template's files to that directory 4. Create a new repository on GitHub and set it as the local repository's upstream 5. Configure relevant settings on the GitHub repository @@ -28,13 +28,13 @@ Hooray! 🥳 ## Options -You can explicitly provide some or all of the options the script would prompt for as command-line flags. +You can customize which pieces of tooling are provided and the options they're created with. See [Options.md](./Options.md). -For example, running the creation script and skipping the _"This package was templated with..."_ block: +For example, skipping the _"This package was templated with..."_ block: ```shell npx create typescript-app --mode create --exclude-templated-with ``` -See [Tooling.md](./Tooling.md) for details on the tooling pieces and which bases they're included in. +See [Blocks.md](./Blocks.md) for details on the tooling pieces and which presets they're included in. diff --git a/docs/Initialization.md b/docs/Initialization.md index 4ec14ead3..2164c75bb 100644 --- a/docs/Initialization.md +++ b/docs/Initialization.md @@ -23,15 +23,13 @@ Hooray! 🥳 ## Options -You can explicitly provide some or all of the options the script would prompt for as command-line flags. +You can customize which pieces of tooling are provided and the options they're created with. See [Options.md](./Options.md). -`pnpm run initialize` will set `--mode` to `initialize`. - -For example, running the creation script and skipping the _"This package was templated with..."_ block: +For example, skipping the _"This package was templated with..."_ block: ```shell -npx create typescript-app --mode create --exclude-templated-with +npx create typescript-app --exclude-templated-with ``` -See [Tooling.md](./Tooling.md) for details on the tooling pieces and which bases they're included in. +See [Blocks.md](./Blocks.md) for details on the tooling pieces and which presets they're included in. diff --git a/docs/Migration.md b/docs/Migration.md index 3526c53ae..e4a01e22c 100644 --- a/docs/Migration.md +++ b/docs/Migration.md @@ -41,13 +41,13 @@ Hooray! 🥳 ## Options -You can explicitly provide some or all of the options the script would prompt for as command-line flags. +You can customize which pieces of tooling are provided and the options they're created with. See [Options.md](./Options.md). -For example, running the migration script and skipping the _"This package was templated with..."_ block: +For example, skipping the _"This package was templated with..."_ block: ```shell -npx create typescript-app --mode migrate --exclude-templated-with +npx create typescript-app --exclude-templated-with ``` -See [Tooling.md](./Tooling.md) for details on the tooling pieces and which bases they're included in. +See [Blocks.md](./Blocks.md) for details on the tooling pieces and which presets they're included in. diff --git a/src/presets/common.ts b/src/presets/common.ts index 8211e4304..d499d04f5 100644 --- a/src/presets/common.ts +++ b/src/presets/common.ts @@ -1,6 +1,7 @@ import { base } from "../base.js"; import { blockAllContributors } from "../blocks/blockAllContributors.js"; import { blockCodecov } from "../blocks/blockCodecov.js"; +import { blockFunding } from "../blocks/blockFunding.js"; import { blockReleaseIt } from "../blocks/blockReleaseIt.js"; import { blockVitest } from "../blocks/blockVitest.js"; import { presetMinimal } from "./minimal.js"; @@ -15,6 +16,7 @@ export const presetCommon = base.createPreset({ ...presetMinimal.blocks, blockAllContributors, blockCodecov, + blockFunding, blockReleaseIt, blockVitest, ], diff --git a/src/presets/everything.ts b/src/presets/everything.ts index 5cff5fc53..d5317f1fc 100644 --- a/src/presets/everything.ts +++ b/src/presets/everything.ts @@ -19,7 +19,6 @@ import { blockPrettierPluginCurly } from "../blocks/blockPrettierPluginCurly.js" import { blockPrettierPluginPackageJson } from "../blocks/blockPrettierPluginPackageJson.js"; import { blockPrettierPluginSh } from "../blocks/blockPrettierPluginSh.js"; import { blockRenovate } from "../blocks/blockRenovate.js"; -import { blockSecurityDocs } from "../blocks/blockSecurityDocs.js"; import { blockVSCode } from "../blocks/blockVSCode.js"; import { presetCommon } from "./common.js"; @@ -51,7 +50,6 @@ export const presetEverything = base.createPreset({ blockPrettierPluginPackageJson, blockPrettierPluginSh, blockRenovate, - blockSecurityDocs, blockVSCode, ], }); diff --git a/src/presets/minimal.ts b/src/presets/minimal.ts index 0ff44447f..2106bd10a 100644 --- a/src/presets/minimal.ts +++ b/src/presets/minimal.ts @@ -4,7 +4,6 @@ import { blockContributorCovenant } from "../blocks/blockContributorCovenant.js" import { blockDevelopmentDocs } from "../blocks/blockDevelopmentDocs.js"; import { blockESLint } from "../blocks/blockESLint.js"; import { blockExampleFiles } from "../blocks/blockExampleFiles.js"; -import { blockFunding } from "../blocks/blockFunding.js"; import { blockGitHubActionsCI } from "../blocks/blockGitHubActionsCI.js"; import { blockGitHubApps } from "../blocks/blockGitHubApps.js"; import { blockGitHubIssueTemplates } from "../blocks/blockGitHubIssueTemplates.js"; @@ -18,6 +17,7 @@ import { blockRepositoryBranchRuleset } from "../blocks/blockRepositoryBranchRul import { blockRepositoryLabels } from "../blocks/blockRepositoryLabels.js"; import { blockRepositorySecrets } from "../blocks/blockRepositorySecrets.js"; import { blockRepositorySettings } from "../blocks/blockRepositorySettings.js"; +import { blockSecurityDocs } from "../blocks/blockSecurityDocs.js"; import { blockTemplatedWith } from "../blocks/blockTemplatedWith.js"; import { blockTSup } from "../blocks/blockTSup.js"; import { blockTypeScript } from "../blocks/blockTypeScript.js"; @@ -34,7 +34,6 @@ export const presetMinimal = base.createPreset({ blockDevelopmentDocs, blockESLint, blockExampleFiles, - blockFunding, blockGitHubActionsCI, blockGitHubApps, blockGitHubIssueTemplates, @@ -48,6 +47,7 @@ export const presetMinimal = base.createPreset({ blockRepositoryLabels, blockRepositorySecrets, blockRepositorySettings, + blockSecurityDocs, blockTemplatedWith, blockTSup, blockTypeScript, From 3b82fed37954175007e1332b8e014ca20127c46a Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 15 Jan 2025 15:10:25 -0500 Subject: [PATCH 17/22] More docs touchups, around blocks and options --- docs/Options.md | 14 ++------------ src/base.ts | 6 ------ src/docs.test.ts | 4 ++-- 3 files changed, 4 insertions(+), 20 deletions(-) diff --git a/docs/Options.md b/docs/Options.md index 8e67bdfe4..803c85147 100644 --- a/docs/Options.md +++ b/docs/Options.md @@ -25,13 +25,11 @@ Per [`create` > CLI > Template Options](https://www.create.bingo/cli#template-op ### Required Base Options -🛑 TODO: Mention `--repository` and `--owner` in `create`, and add _"you can't override these"_ issue - These options can only be inferred when running on an existing repository. Each will be prompted for when creating a new repository if not explicitly provided: -- `--description` _(`string`)_: Sentence case description of the repository -- `--title` _(`string`)_: Title Case title for the repository +- `--description` _(`string`)_: 'Sentence case.' description of the repository +- `--title` _(`string`)_: 'Title Case' title for the repository For example, pre-populating both required base options: @@ -51,17 +49,9 @@ They will be inferred from the running user, and if migrating an existing reposi - `--bin` _(`string`)_: Value to set in `package.json`'s `"bin"` property, per [FAQs > How can I use `bin`?](./FAQs.md#how-can-i-use-bin) - `--directory` _(`string`)_: Directory to create the repository in (by default, the same name as the repository) - `--email` _(`string`)_: Email address to be listed as the point of contact in docs and packages (e.g. `example@joshuakgoldberg.com`) - - Optionally, `--email-github` _(`string`)_ and/or `--email-npm` _(`string`)_ may be provided to use different emails in `.md` files and `package.json`, respectively - `--funding` _(`string`)_: GitHub organization or username to mention in `funding.yml` (by default, `owner`) -- `--guide` _(`string`)_: Link to a contribution guide to place at the top of development docs - - `--guide-title` _(`string`)_: If `--guide` is provided or detected from an existing DEVELOPMENT.md, the text title to place in the guide link - `--keywords` _(`string[]`)_: Any number of keywords to include in `package.json` (by default, none) - This can be specified any number of times, like `--keywords apple --keywords "banana cherry"` -- `--logo` _(`string`)_: Local image file in the repository to display near the top of the README.md - - `--logo-alt` _(`string`)_: If `--logo` is provided or detected from an existing README.md, alt text that describes the image (will be prompted for if not provided) - - `--logo-height` _(`number`)_: If `--logo` is provided or detected from an existing README.md, an explicit height style (by default, read from the image, capped to `128`) - - `--logo-width` _(`number`)_: If `--logo` is provided or detected from an existing README.md, an explicit width style (by default, read from the image, capped to `128`) -- `--preserve-generated-from` _(`boolean`)_: Whether to keep the GitHub repository _generated from_ notice (by default, `false`) For example, customizing the npm author and funding source: diff --git a/src/base.ts b/src/base.ts index 75626c53d..7185d8427 100644 --- a/src/base.ts +++ b/src/base.ts @@ -89,12 +89,6 @@ export const base = createBase({ .describe( "link to a contribution guide to place at the top of development docs", ), - hideTemplatedBy: z - .boolean() - .optional() - .describe( - "whether to hide the 'created by ...' notice at the bottom of the README.md", - ), keywords: z .array(z.string()) .optional() diff --git a/src/docs.test.ts b/src/docs.test.ts index 0c1273c02..4fcc0b160 100644 --- a/src/docs.test.ts +++ b/src/docs.test.ts @@ -8,8 +8,8 @@ import { blocks, presets } from "./index.js"; const actualLines = await createActualLines(); const expectedLines = await createExpectedLines(); -// This test ensures ensures docs/Blocks.md has a row for each of CTA's options. -// Each row should include emojis describing which preset(s) include the option. +// This test ensures ensures docs/Blocks.md has a row for each of CTA's blocks. +// Each row should include emojis describing which preset(s) include the block. // // If this fails, it's likely due to adding, removing, or renaming a block. // You may need to manually change docs/Blocks.md to match to those changes. From e8f76aeae050ab362209e254349b8a0f1f0a08e2 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 15 Jan 2025 15:58:16 -0500 Subject: [PATCH 18/22] fix: pipe GH Actions CI jobs addons to ruleset contexts --- src/blocks/blockGitHubActionsCI.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/blocks/blockGitHubActionsCI.ts b/src/blocks/blockGitHubActionsCI.ts index 073eba4f5..da2cb94f5 100644 --- a/src/blocks/blockGitHubActionsCI.ts +++ b/src/blocks/blockGitHubActionsCI.ts @@ -2,6 +2,7 @@ import jsYaml from "js-yaml"; import { z } from "zod"; import { base } from "../base.js"; +import { blockRepositoryBranchRuleset } from "./blockRepositoryBranchRuleset.js"; import { createMultiWorkflowFile } from "./files/createMultiWorkflowFile.js"; import { createSoloWorkflowFile } from "./files/createSoloWorkflowFile.js"; import { CommandPhase } from "./phases.js"; @@ -43,6 +44,11 @@ export const blockGitHubActionsCI = base.createBlock({ const { jobs } = addons; return { + addons: [ + blockRepositoryBranchRuleset({ + requiredStatusChecks: jobs?.map((job) => job.name), + }), + ], files: { ".github": { actions: { From 24fba7ecb0bb55de3dd21fe6e782c0eb047bb367 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 15 Jan 2025 15:58:32 -0500 Subject: [PATCH 19/22] fix: blockPackageJson correct pnpm install --offline... ugh. --- src/blocks/blockPackageJson.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blocks/blockPackageJson.ts b/src/blocks/blockPackageJson.ts index 3629977c7..9fddaa98d 100644 --- a/src/blocks/blockPackageJson.ts +++ b/src/blocks/blockPackageJson.ts @@ -100,7 +100,7 @@ export const blockPackageJson = base.createBlock({ scripts: [ { commands: [ - offline ? "pnpm install" : "pnpm install --offline", + offline ? "pnpm install --offline" : "pnpm install", ...addons.cleanupCommands, ], phase: CommandPhase.Install, From 64615992a4c4ec554a315e73dcf2250a2852fe2e Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 15 Jan 2025 16:54:23 -0500 Subject: [PATCH 20/22] Remove other test package.json dependencies in blockVitest --- docs/Migration.md | 1 - knip.json | 6 +++++- package.json | 1 + pnpm-lock.yaml | 10 ++++++++++ src/blocks/blockGitHubActionsCI.test.ts | 26 +++++++++++++++++++++++++ src/blocks/blockPackageJson.test.ts | 8 ++++---- src/blocks/blockVitest.test.ts | 13 ++++++++++++- src/blocks/blockVitest.ts | 8 ++++++++ src/integration.test.ts | 6 +++++- 9 files changed, 71 insertions(+), 8 deletions(-) diff --git a/docs/Migration.md b/docs/Migration.md index e4a01e22c..0fa226b46 100644 --- a/docs/Migration.md +++ b/docs/Migration.md @@ -17,7 +17,6 @@ The migration script will: For example, if the repository previously using Jest for testing: - `eslint-plugin-jest`, `jest`, and other Jest-related packages will be uninstalled - - 🛑 TODO: Missing in `blockVitest` - Any Jest config file like `jest.config.js` will be deleted - `@vitest/eslint-plugin`, `vitest`, and other Vitest-related packages will be installed - A `vitest.config.ts` file will be created diff --git a/knip.json b/knip.json index 16c4fc7e1..55408fe8e 100644 --- a/knip.json +++ b/knip.json @@ -1,7 +1,11 @@ { "$schema": "https://unpkg.com/knip@5.41.1/schema.json", "entry": ["src/index.ts", "src/**/*.test.*"], - "ignoreDependencies": ["all-contributors-cli", "cspell-populate-words"], + "ignoreDependencies": [ + "all-contributors-cli", + "cspell-populate-words", + "remove-dependencies" + ], "ignoreExportsUsedInFile": { "interface": true, "type": true }, "project": ["src/**/*.ts"] } diff --git a/package.json b/package.json index 28aaf7772..2abd69784 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "object-strings-deep": "^0.1.1", "parse-author": "^2.0.0", "parse-package-name": "^1.0.0", + "remove-dependencies": "^0.1.0", "remove-undefined-objects": "^5.0.0", "set-github-repository-labels": "^0.1.0", "sort-package-json": "^2.12.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dabe8f3ee..9ce5cef77 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -65,6 +65,9 @@ importers: parse-package-name: specifier: ^1.0.0 version: 1.0.0 + remove-dependencies: + specifier: ^0.1.0 + version: 0.1.0 remove-undefined-objects: specifier: ^5.0.0 version: 5.0.0 @@ -3676,6 +3679,11 @@ packages: engines: {node: ^18.18.0 || ^20.9.0 || ^22.0.0} hasBin: true + remove-dependencies@0.1.0: + resolution: {integrity: sha512-K9PXkqGYkuQbRVf57SI/t3xBhw87pLbvd94zir6v4+ptJQ0TSjLWtd0ZlebtQEOfnxSchz+wgzTvG7KNpLMAbg==} + engines: {node: '>=18.3.0'} + hasBin: true + remove-undefined-objects@5.0.0: resolution: {integrity: sha512-DE8C17uIWeHaY4SqIkpQpHXm0MIdYHtIqjieWuh0I2PG8YcZRxFE6pqeEhnRetsrQ7Lu9uvSNQkDbg95NLpvnQ==} engines: {node: '>=18'} @@ -8054,6 +8062,8 @@ snapshots: - supports-color - typescript + remove-dependencies@0.1.0: {} + remove-undefined-objects@5.0.0: {} repeat-string@1.6.1: {} diff --git a/src/blocks/blockGitHubActionsCI.test.ts b/src/blocks/blockGitHubActionsCI.test.ts index 1cc55e004..2e373921f 100644 --- a/src/blocks/blockGitHubActionsCI.test.ts +++ b/src/blocks/blockGitHubActionsCI.test.ts @@ -12,6 +12,14 @@ describe("blockGitHubActionsCI", () => { expect(creation).toMatchInlineSnapshot(` { + "addons": [ + { + "addons": { + "requiredStatusChecks": undefined, + }, + "block": [Function], + }, + ], "files": { ".github": { "actions": { @@ -101,6 +109,14 @@ describe("blockGitHubActionsCI", () => { expect(creation).toMatchInlineSnapshot(` { + "addons": [ + { + "addons": { + "requiredStatusChecks": undefined, + }, + "block": [Function], + }, + ], "files": { ".github": { "actions": { @@ -212,6 +228,16 @@ describe("blockGitHubActionsCI", () => { expect(creation).toMatchInlineSnapshot(` { + "addons": [ + { + "addons": { + "requiredStatusChecks": [ + "Validate", + ], + }, + "block": [Function], + }, + ], "files": { ".github": { "actions": { diff --git a/src/blocks/blockPackageJson.test.ts b/src/blocks/blockPackageJson.test.ts index 7e9bf3b24..49c954aa7 100644 --- a/src/blocks/blockPackageJson.test.ts +++ b/src/blocks/blockPackageJson.test.ts @@ -21,7 +21,7 @@ describe("blockPackageJson", () => { "scripts": [ { "commands": [ - "pnpm install --offline", + "pnpm install", ], "phase": 1, }, @@ -44,7 +44,7 @@ describe("blockPackageJson", () => { "scripts": [ { "commands": [ - "pnpm install --offline", + "pnpm install", ], "phase": 1, }, @@ -81,7 +81,7 @@ describe("blockPackageJson", () => { "scripts": [ { "commands": [ - "pnpm install --offline", + "pnpm install", "pnpm dedupe", ], "phase": 1, @@ -116,7 +116,7 @@ describe("blockPackageJson", () => { "scripts": [ { "commands": [ - "pnpm install --offline", + "pnpm install", "pnpm dedupe", ], "phase": 1, diff --git a/src/blocks/blockVitest.test.ts b/src/blocks/blockVitest.test.ts index e942a6e66..1dd05cb97 100644 --- a/src/blocks/blockVitest.test.ts +++ b/src/blocks/blockVitest.test.ts @@ -1,9 +1,13 @@ import { testBlock } from "create-testers"; -import { describe, expect, test } from "vitest"; +import { describe, expect, test, vi } from "vitest"; import { blockVitest } from "./blockVitest.js"; import { optionsBase } from "./options.fakes.js"; +vi.mock("../utils/resolveBin.js", () => ({ + resolveBin: (bin: string) => `path/to/${bin}`, +})); + describe("blockVitest", () => { test("without addons or mode", () => { const creation = testBlock(blockVitest, { @@ -467,6 +471,13 @@ describe("blockVitest", () => { ", }, "scripts": [ + { + "commands": [ + "node path/to/remove-dependencies/bin/index.mjs eslint-plugin-jest eslint-plugin-mocha eslint-plugin-vitest jest mocha", + ], + "phase": 3, + "silent": true, + }, { "commands": [ "rm .mocha* jest.config.* vitest.config.*", diff --git a/src/blocks/blockVitest.ts b/src/blocks/blockVitest.ts index b5abbadb0..748671252 100644 --- a/src/blocks/blockVitest.ts +++ b/src/blocks/blockVitest.ts @@ -2,6 +2,7 @@ import { z } from "zod"; import { base } from "../base.js"; import { getPackageDependencies } from "../data/packageData.js"; +import { resolveBin } from "../utils/resolveBin.js"; import { blockCSpell } from "./blockCSpell.js"; import { blockDevelopmentDocs } from "./blockDevelopmentDocs.js"; import { blockESLint } from "./blockESLint.js"; @@ -32,6 +33,13 @@ export const blockVitest = base.createBlock({ migrate() { return { scripts: [ + { + commands: [ + `node ${resolveBin("remove-dependencies/bin/index.mjs")} eslint-plugin-jest eslint-plugin-mocha eslint-plugin-vitest jest mocha`, + ], + phase: CommandPhase.Process, + silent: true, + }, { commands: ["rm .mocha* jest.config.* vitest.config.*"], phase: CommandPhase.Migrations, diff --git a/src/integration.test.ts b/src/integration.test.ts index 1ee3b31b7..90992bbf6 100644 --- a/src/integration.test.ts +++ b/src/integration.test.ts @@ -85,7 +85,11 @@ If you're interested in learning more, see the 'getting started' docs on: ], }), blockKnip({ - ignoreDependencies: ["all-contributors-cli", "cspell-populate-words"], + ignoreDependencies: [ + "all-contributors-cli", + "cspell-populate-words", + "remove-dependencies", + ], }), blockTSup({ runArgs: ["--version"], From a98eec32272f13dc1b48c4880b80e4b69dab3553 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 15 Jan 2025 17:16:35 -0500 Subject: [PATCH 21/22] fix: silent scripts --- package.json | 2 +- pnpm-lock.yaml | 36 ++++++++++----------- src/blocks/blockCodecov.test.ts | 1 + src/blocks/blockCodecov.ts | 1 + src/blocks/blockContributingDocs.test.ts | 1 + src/blocks/blockContributingDocs.ts | 1 + src/blocks/blockContributorCovenant.test.ts | 1 + src/blocks/blockContributorCovenant.ts | 1 + src/blocks/blockDevelopmentDocs.test.ts | 1 + src/blocks/blockDevelopmentDocs.ts | 1 + src/blocks/blockESLint.test.ts | 1 + src/blocks/blockESLint.ts | 1 + src/blocks/blockGitHubActionsCI.test.ts | 1 + src/blocks/blockGitHubActionsCI.ts | 1 + src/blocks/blockKnip.ts | 1 + src/blocks/blockPackageJson.test.ts | 1 + src/blocks/blockPackageJson.ts | 1 + src/blocks/blockPrettier.test.ts | 1 + src/blocks/blockPrettier.ts | 1 + src/blocks/blockTSup.test.ts | 1 + src/blocks/blockTSup.ts | 1 + src/blocks/blockVitest.test.ts | 1 + src/blocks/blockVitest.ts | 1 + 23 files changed, 40 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index 2abd69784..2c7e5fd48 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ }, "dependencies": { "chalk": "^5.4.1", - "create": "0.1.0-alpha.14", + "create": "0.1.0-alpha.15", "create-fs": "^0.1.1", "cspell-populate-words": "^0.3.0", "execa": "^9.5.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9ce5cef77..9152cbd51 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,8 +12,8 @@ importers: specifier: ^5.4.1 version: 5.4.1 create: - specifier: 0.1.0-alpha.14 - version: 0.1.0-alpha.14 + specifier: 0.1.0-alpha.15 + version: 0.1.0-alpha.15 create-fs: specifier: ^0.1.1 version: 0.1.1 @@ -37,13 +37,13 @@ importers: version: 1.2.0 input-from-file: specifier: 0.1.0-alpha.4 - version: 0.1.0-alpha.4(create@0.1.0-alpha.14) + version: 0.1.0-alpha.4(create@0.1.0-alpha.15) input-from-file-json: specifier: 0.1.0-alpha.4 - version: 0.1.0-alpha.4(create@0.1.0-alpha.14) + version: 0.1.0-alpha.4(create@0.1.0-alpha.15) input-from-script: specifier: 0.1.0-alpha.4 - version: 0.1.0-alpha.4(create@0.1.0-alpha.14) + version: 0.1.0-alpha.4(create@0.1.0-alpha.15) js-yaml: specifier: ^4.1.0 version: 4.1.0 @@ -131,7 +131,7 @@ importers: version: 0.5.0 create-testers: specifier: 0.1.0-alpha.14 - version: 0.1.0-alpha.14(create-fs@0.1.1)(create@0.1.0-alpha.14) + version: 0.1.0-alpha.14(create-fs@0.1.1)(create@0.1.0-alpha.15) cspell: specifier: ^8.17.2 version: 8.17.2 @@ -1933,8 +1933,8 @@ packages: create: ^0.1.0-alpha.14 create-fs: ^0.1.0 - create@0.1.0-alpha.14: - resolution: {integrity: sha512-OExXy9Tn7Nugayz/eKMJV1/oM7J/p2SOi3HK/zEyMXNwF2IwnPiHli6rjyvl5hggU7618cumdD6D5ptJK1Zixw==} + create@0.1.0-alpha.15: + resolution: {integrity: sha512-iTBTuTIv+f+nD9mr7hyKrKuPyw46UDDBVz0iU7mCnq5MW/0OUqwm7oTFrQ6GTggDiYiS+7Yz3/AZ92EKKZVadA==} engines: {node: '>=18'} hasBin: true @@ -5945,15 +5945,15 @@ snapshots: create-fs@0.1.1: {} - create-testers@0.1.0-alpha.14(create-fs@0.1.1)(create@0.1.0-alpha.14): + create-testers@0.1.0-alpha.14(create-fs@0.1.1)(create@0.1.0-alpha.15): dependencies: - create: 0.1.0-alpha.14 + create: 0.1.0-alpha.15 create-fs: 0.1.1 diff: 7.0.0 octokit: 4.1.0 without-undefined-properties: 0.1.1 - create@0.1.0-alpha.14: + create@0.1.0-alpha.15: dependencies: '@clack/prompts': 0.9.1 cached-factory: 0.1.0 @@ -6909,20 +6909,20 @@ snapshots: ini@4.1.1: {} - input-from-file-json@0.1.0-alpha.4(create@0.1.0-alpha.14): + input-from-file-json@0.1.0-alpha.4(create@0.1.0-alpha.15): dependencies: - create: 0.1.0-alpha.14 - input-from-file: 0.1.0-alpha.4(create@0.1.0-alpha.14) + create: 0.1.0-alpha.15 + input-from-file: 0.1.0-alpha.4(create@0.1.0-alpha.15) zod: 3.24.1 - input-from-file@0.1.0-alpha.4(create@0.1.0-alpha.14): + input-from-file@0.1.0-alpha.4(create@0.1.0-alpha.15): dependencies: - create: 0.1.0-alpha.14 + create: 0.1.0-alpha.15 zod: 3.24.1 - input-from-script@0.1.0-alpha.4(create@0.1.0-alpha.14): + input-from-script@0.1.0-alpha.4(create@0.1.0-alpha.15): dependencies: - create: 0.1.0-alpha.14 + create: 0.1.0-alpha.15 zod: 3.24.1 inquirer@7.3.3: diff --git a/src/blocks/blockCodecov.test.ts b/src/blocks/blockCodecov.test.ts index 878e424c3..185f91f67 100644 --- a/src/blocks/blockCodecov.test.ts +++ b/src/blocks/blockCodecov.test.ts @@ -78,6 +78,7 @@ describe("blockCodecov", () => { "rm .github/codecov.yml codecov.yml", ], "phase": 0, + "silent": true, }, ], } diff --git a/src/blocks/blockCodecov.ts b/src/blocks/blockCodecov.ts index 36203aa96..2233296a2 100644 --- a/src/blocks/blockCodecov.ts +++ b/src/blocks/blockCodecov.ts @@ -16,6 +16,7 @@ export const blockCodecov = base.createBlock({ { commands: ["rm .github/codecov.yml codecov.yml"], phase: CommandPhase.Migrations, + silent: true, }, ], }; diff --git a/src/blocks/blockContributingDocs.test.ts b/src/blocks/blockContributingDocs.test.ts index 115ab626e..869d7472d 100644 --- a/src/blocks/blockContributingDocs.test.ts +++ b/src/blocks/blockContributingDocs.test.ts @@ -234,6 +234,7 @@ describe("blockContributingDocs", () => { "rm CONTRIBUTING.md", ], "phase": 0, + "silent": true, }, ], } diff --git a/src/blocks/blockContributingDocs.ts b/src/blocks/blockContributingDocs.ts index 99e30a767..0e68dc515 100644 --- a/src/blocks/blockContributingDocs.ts +++ b/src/blocks/blockContributingDocs.ts @@ -11,6 +11,7 @@ export const blockContributingDocs = base.createBlock({ { commands: ["rm CONTRIBUTING.md"], phase: CommandPhase.Migrations, + silent: true, }, ], }; diff --git a/src/blocks/blockContributorCovenant.test.ts b/src/blocks/blockContributorCovenant.test.ts index e04d232c0..0904a8921 100644 --- a/src/blocks/blockContributorCovenant.test.ts +++ b/src/blocks/blockContributorCovenant.test.ts @@ -304,6 +304,7 @@ describe("blockContributorCovenant", () => { "rm CODE_OF_CONDUCT.md", ], "phase": 0, + "silent": true, }, ], } diff --git a/src/blocks/blockContributorCovenant.ts b/src/blocks/blockContributorCovenant.ts index 5254b5026..996ec8833 100644 --- a/src/blocks/blockContributorCovenant.ts +++ b/src/blocks/blockContributorCovenant.ts @@ -11,6 +11,7 @@ export const blockContributorCovenant = base.createBlock({ { commands: ["rm CODE_OF_CONDUCT.md"], phase: CommandPhase.Migrations, + silent: true, }, ], }; diff --git a/src/blocks/blockDevelopmentDocs.test.ts b/src/blocks/blockDevelopmentDocs.test.ts index e07c4c126..659faecea 100644 --- a/src/blocks/blockDevelopmentDocs.test.ts +++ b/src/blocks/blockDevelopmentDocs.test.ts @@ -58,6 +58,7 @@ describe("blockDevelopmentDocs", () => { "rm DEVELOPMENT.md", ], "phase": 0, + "silent": true, }, ], } diff --git a/src/blocks/blockDevelopmentDocs.ts b/src/blocks/blockDevelopmentDocs.ts index fa3e40756..13dcc8986 100644 --- a/src/blocks/blockDevelopmentDocs.ts +++ b/src/blocks/blockDevelopmentDocs.ts @@ -70,6 +70,7 @@ export const blockDevelopmentDocs = base.createBlock({ { commands: ["rm DEVELOPMENT.md"], phase: CommandPhase.Migrations, + silent: true, }, ], }; diff --git a/src/blocks/blockESLint.test.ts b/src/blocks/blockESLint.test.ts index 1f93d2c89..bc6cbf551 100644 --- a/src/blocks/blockESLint.test.ts +++ b/src/blocks/blockESLint.test.ts @@ -262,6 +262,7 @@ describe("blockESLint", () => { "rm .eslintrc* .eslintignore eslint.config.*", ], "phase": 0, + "silent": true, }, ], } diff --git a/src/blocks/blockESLint.ts b/src/blocks/blockESLint.ts index f2e83f318..663934fd1 100644 --- a/src/blocks/blockESLint.ts +++ b/src/blocks/blockESLint.ts @@ -70,6 +70,7 @@ export const blockESLint = base.createBlock({ { commands: ["rm .eslintrc* .eslintignore eslint.config.*"], phase: CommandPhase.Migrations, + silent: true, }, ], }; diff --git a/src/blocks/blockGitHubActionsCI.test.ts b/src/blocks/blockGitHubActionsCI.test.ts index 2e373921f..cbf7801f5 100644 --- a/src/blocks/blockGitHubActionsCI.test.ts +++ b/src/blocks/blockGitHubActionsCI.test.ts @@ -200,6 +200,7 @@ describe("blockGitHubActionsCI", () => { "rm -rf .circleci travis.yml", ], "phase": 0, + "silent": true, }, ], } diff --git a/src/blocks/blockGitHubActionsCI.ts b/src/blocks/blockGitHubActionsCI.ts index da2cb94f5..1da0d2289 100644 --- a/src/blocks/blockGitHubActionsCI.ts +++ b/src/blocks/blockGitHubActionsCI.ts @@ -36,6 +36,7 @@ export const blockGitHubActionsCI = base.createBlock({ { commands: ["rm -rf .circleci travis.yml"], phase: CommandPhase.Migrations, + silent: true, }, ], }; diff --git a/src/blocks/blockKnip.ts b/src/blocks/blockKnip.ts index 411e1708e..82585d554 100644 --- a/src/blocks/blockKnip.ts +++ b/src/blocks/blockKnip.ts @@ -23,6 +23,7 @@ export const blockKnip = base.createBlock({ { commands: ["rm .knip* knip.*"], phase: CommandPhase.Migrations, + silent: true, }, ], }; diff --git a/src/blocks/blockPackageJson.test.ts b/src/blocks/blockPackageJson.test.ts index 49c954aa7..56801d9f2 100644 --- a/src/blocks/blockPackageJson.test.ts +++ b/src/blocks/blockPackageJson.test.ts @@ -53,6 +53,7 @@ describe("blockPackageJson", () => { "rm package-lock.json yarn.lock", ], "phase": 0, + "silent": true, }, ], } diff --git a/src/blocks/blockPackageJson.ts b/src/blocks/blockPackageJson.ts index 9fddaa98d..6ae6bc46b 100644 --- a/src/blocks/blockPackageJson.ts +++ b/src/blocks/blockPackageJson.ts @@ -32,6 +32,7 @@ export const blockPackageJson = base.createBlock({ { commands: ["rm package-lock.json yarn.lock"], phase: CommandPhase.Migrations, + silent: true, }, ], }; diff --git a/src/blocks/blockPrettier.test.ts b/src/blocks/blockPrettier.test.ts index 7c36aa44a..ea0fb6474 100644 --- a/src/blocks/blockPrettier.test.ts +++ b/src/blocks/blockPrettier.test.ts @@ -228,6 +228,7 @@ describe("blockPrettier", () => { "rm .prettierrc* prettier.config*", ], "phase": 0, + "silent": true, }, ], } diff --git a/src/blocks/blockPrettier.ts b/src/blocks/blockPrettier.ts index d54671820..e385cd658 100644 --- a/src/blocks/blockPrettier.ts +++ b/src/blocks/blockPrettier.ts @@ -34,6 +34,7 @@ export const blockPrettier = base.createBlock({ { commands: ["rm .prettierrc* prettier.config*"], phase: CommandPhase.Migrations, + silent: true, }, ], }; diff --git a/src/blocks/blockTSup.test.ts b/src/blocks/blockTSup.test.ts index 1cfbe508b..fd7a10225 100644 --- a/src/blocks/blockTSup.test.ts +++ b/src/blocks/blockTSup.test.ts @@ -181,6 +181,7 @@ describe("blockTSup", () => { "rm -rf .babelrc* babel.config.* dist lib", ], "phase": 0, + "silent": true, }, ], } diff --git a/src/blocks/blockTSup.ts b/src/blocks/blockTSup.ts index 43d418ba1..171bc2e29 100644 --- a/src/blocks/blockTSup.ts +++ b/src/blocks/blockTSup.ts @@ -22,6 +22,7 @@ export const blockTSup = base.createBlock({ { commands: ["rm -rf .babelrc* babel.config.* dist lib"], phase: CommandPhase.Migrations, + silent: true, }, ], }; diff --git a/src/blocks/blockVitest.test.ts b/src/blocks/blockVitest.test.ts index 1dd05cb97..fdd06f467 100644 --- a/src/blocks/blockVitest.test.ts +++ b/src/blocks/blockVitest.test.ts @@ -483,6 +483,7 @@ describe("blockVitest", () => { "rm .mocha* jest.config.* vitest.config.*", ], "phase": 0, + "silent": true, }, ], } diff --git a/src/blocks/blockVitest.ts b/src/blocks/blockVitest.ts index 748671252..96d3a7a05 100644 --- a/src/blocks/blockVitest.ts +++ b/src/blocks/blockVitest.ts @@ -43,6 +43,7 @@ export const blockVitest = base.createBlock({ { commands: ["rm .mocha* jest.config.* vitest.config.*"], phase: CommandPhase.Migrations, + silent: true, }, ], }; From d89754f0bd4367b295da2c1dade013b758f773cf Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 15 Jan 2025 17:19:18 -0500 Subject: [PATCH 22/22] fix: remove-dependencies/bin/index.js --- src/blocks/blockVitest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blocks/blockVitest.ts b/src/blocks/blockVitest.ts index 96d3a7a05..2ef2097df 100644 --- a/src/blocks/blockVitest.ts +++ b/src/blocks/blockVitest.ts @@ -35,7 +35,7 @@ export const blockVitest = base.createBlock({ scripts: [ { commands: [ - `node ${resolveBin("remove-dependencies/bin/index.mjs")} eslint-plugin-jest eslint-plugin-mocha eslint-plugin-vitest jest mocha`, + `node ${resolveBin("remove-dependencies/bin/index.js")} eslint-plugin-jest eslint-plugin-mocha eslint-plugin-vitest jest mocha`, ], phase: CommandPhase.Process, silent: true,