create-krispya 0.5.1 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +106 -8
- package/dist/chunks/index.cjs +581 -175
- package/dist/chunks/index.mjs +571 -176
- package/dist/cli.cjs +1078 -317
- package/dist/cli.mjs +1082 -321
- package/dist/index.cjs +4 -0
- package/dist/index.d.cts +46 -1
- package/dist/index.d.mts +46 -1
- package/dist/index.d.ts +46 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -159,6 +159,12 @@ function getReuseWindow() {
|
|
|
159
159
|
function setReuseWindow(reuse) {
|
|
160
160
|
config.set("reuseWindow", reuse);
|
|
161
161
|
}
|
|
162
|
+
function getAiFiles() {
|
|
163
|
+
return config.get("aiFiles");
|
|
164
|
+
}
|
|
165
|
+
function setAiFiles(files) {
|
|
166
|
+
config.set("aiFiles", files);
|
|
167
|
+
}
|
|
162
168
|
function clearConfig() {
|
|
163
169
|
config.clear();
|
|
164
170
|
}
|
|
@@ -446,31 +452,14 @@ async function promptForMonorepoCustomization(name) {
|
|
|
446
452
|
p__namespace.cancel("Operation cancelled.");
|
|
447
453
|
process.exit(0);
|
|
448
454
|
}
|
|
449
|
-
const
|
|
450
|
-
message: "
|
|
451
|
-
|
|
452
|
-
{ value: "pnpm", label: "pnpm" },
|
|
453
|
-
{ value: "npm", label: "npm" },
|
|
454
|
-
{ value: "yarn", label: "yarn" }
|
|
455
|
-
],
|
|
456
|
-
initialValue: "pnpm"
|
|
455
|
+
const managePnpm = await p__namespace.confirm({
|
|
456
|
+
message: "Enable manage-package-manager-versions?",
|
|
457
|
+
initialValue: true
|
|
457
458
|
});
|
|
458
|
-
if (p__namespace.isCancel(
|
|
459
|
+
if (p__namespace.isCancel(managePnpm)) {
|
|
459
460
|
p__namespace.cancel("Operation cancelled.");
|
|
460
461
|
process.exit(0);
|
|
461
462
|
}
|
|
462
|
-
let pnpmManageVersions = true;
|
|
463
|
-
if (packageManager === "pnpm") {
|
|
464
|
-
const managePnpm = await p__namespace.confirm({
|
|
465
|
-
message: "Enable manage-package-manager-versions?",
|
|
466
|
-
initialValue: true
|
|
467
|
-
});
|
|
468
|
-
if (p__namespace.isCancel(managePnpm)) {
|
|
469
|
-
p__namespace.cancel("Operation cancelled.");
|
|
470
|
-
process.exit(0);
|
|
471
|
-
}
|
|
472
|
-
pnpmManageVersions = managePnpm;
|
|
473
|
-
}
|
|
474
463
|
const linter = await p__namespace.select({
|
|
475
464
|
message: "Linter",
|
|
476
465
|
options: [
|
|
@@ -501,8 +490,8 @@ async function promptForMonorepoCustomization(name) {
|
|
|
501
490
|
name,
|
|
502
491
|
projectType: "monorepo",
|
|
503
492
|
nodeVersion,
|
|
504
|
-
packageManager,
|
|
505
|
-
pnpmManageVersions,
|
|
493
|
+
packageManager: "pnpm",
|
|
494
|
+
pnpmManageVersions: managePnpm,
|
|
506
495
|
linter,
|
|
507
496
|
formatter
|
|
508
497
|
};
|
|
@@ -686,8 +675,79 @@ async function promptForPackageOptions(projectName, projectType, inheritedToolin
|
|
|
686
675
|
return promptForCustomization(template, projectName, projectType, integrations, inheritedTooling);
|
|
687
676
|
}
|
|
688
677
|
|
|
678
|
+
async function checkAnyExists(paths) {
|
|
679
|
+
for (const path of paths) {
|
|
680
|
+
try {
|
|
681
|
+
await promises.access(path, promises.constants.F_OK);
|
|
682
|
+
return true;
|
|
683
|
+
} catch {
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
return false;
|
|
687
|
+
}
|
|
688
|
+
async function validateWorkspace(monorepoRoot) {
|
|
689
|
+
const errors = [];
|
|
690
|
+
const tsConfigPath = path.join(monorepoRoot, ".config/typescript/package.json");
|
|
691
|
+
try {
|
|
692
|
+
await promises.access(tsConfigPath, promises.constants.F_OK);
|
|
693
|
+
} catch {
|
|
694
|
+
errors.push("Missing .config/typescript package");
|
|
695
|
+
}
|
|
696
|
+
const linterPaths = [
|
|
697
|
+
path.join(monorepoRoot, ".config/oxlint/package.json"),
|
|
698
|
+
path.join(monorepoRoot, ".config/eslint/package.json"),
|
|
699
|
+
path.join(monorepoRoot, "eslint.config.js"),
|
|
700
|
+
path.join(monorepoRoot, "biome.json")
|
|
701
|
+
];
|
|
702
|
+
const hasLinter = await checkAnyExists(linterPaths);
|
|
703
|
+
if (!hasLinter) {
|
|
704
|
+
errors.push(
|
|
705
|
+
"Missing linter config (.config/oxlint, .config/eslint, eslint.config.js, or biome.json)"
|
|
706
|
+
);
|
|
707
|
+
}
|
|
708
|
+
const formatterPaths = [
|
|
709
|
+
path.join(monorepoRoot, ".config/oxfmt/package.json"),
|
|
710
|
+
path.join(monorepoRoot, ".config/prettier/package.json"),
|
|
711
|
+
path.join(monorepoRoot, ".prettierrc.json"),
|
|
712
|
+
path.join(monorepoRoot, "biome.json")
|
|
713
|
+
];
|
|
714
|
+
const hasFormatter = await checkAnyExists(formatterPaths);
|
|
715
|
+
if (!hasFormatter) {
|
|
716
|
+
errors.push(
|
|
717
|
+
"Missing formatter config (.config/oxfmt, .config/prettier, .prettierrc.json, or biome.json)"
|
|
718
|
+
);
|
|
719
|
+
}
|
|
720
|
+
return { valid: errors.length === 0, errors };
|
|
721
|
+
}
|
|
722
|
+
|
|
689
723
|
const require$1 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli.cjs', document.baseURI).href)));
|
|
690
724
|
const pkg = require$1("../package.json");
|
|
725
|
+
async function fileExists(path) {
|
|
726
|
+
try {
|
|
727
|
+
await promises.access(path, fs.constants.F_OK);
|
|
728
|
+
return true;
|
|
729
|
+
} catch {
|
|
730
|
+
return false;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
async function writeGeneratedFiles(basePath, files) {
|
|
734
|
+
const filePaths = Object.keys(files).sort();
|
|
735
|
+
for (const filePath of filePaths) {
|
|
736
|
+
const fullFilePath = path.join(basePath, filePath);
|
|
737
|
+
await promises.mkdir(path.dirname(fullFilePath), { recursive: true });
|
|
738
|
+
const file = files[filePath];
|
|
739
|
+
if (file.type === "text") {
|
|
740
|
+
await promises.writeFile(fullFilePath, file.content);
|
|
741
|
+
} else {
|
|
742
|
+
const response = await undici.fetch(file.url);
|
|
743
|
+
await promises.writeFile(fullFilePath, response.body);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
function calculateWorkspaceRoot(packagePath) {
|
|
748
|
+
const segments = packagePath.split(/[/\\]/).filter(Boolean);
|
|
749
|
+
return segments.map(() => "..").join("/");
|
|
750
|
+
}
|
|
691
751
|
async function detectMonorepoRoot() {
|
|
692
752
|
let currentDir = process$1.cwd();
|
|
693
753
|
const root = path.resolve("/");
|
|
@@ -705,12 +765,21 @@ async function detectMonorepoRoot() {
|
|
|
705
765
|
}
|
|
706
766
|
return null;
|
|
707
767
|
}
|
|
768
|
+
async function parseWorkspaceDirectories(monorepoRoot) {
|
|
769
|
+
try {
|
|
770
|
+
const workspaceFile = path.join(monorepoRoot, "pnpm-workspace.yaml");
|
|
771
|
+
const content = await promises.readFile(workspaceFile, "utf-8");
|
|
772
|
+
return index.parseWorkspaceYamlContent(content);
|
|
773
|
+
} catch {
|
|
774
|
+
return [];
|
|
775
|
+
}
|
|
776
|
+
}
|
|
708
777
|
async function detectWorkspaceTooling(monorepoRoot) {
|
|
709
778
|
try {
|
|
710
779
|
const pkgPath = path.join(monorepoRoot, "package.json");
|
|
711
780
|
const content = await promises.readFile(pkgPath, "utf-8");
|
|
712
|
-
const
|
|
713
|
-
const devDeps =
|
|
781
|
+
const pkgJson = JSON.parse(content);
|
|
782
|
+
const devDeps = pkgJson.devDependencies ?? {};
|
|
714
783
|
const linter = devDeps.oxlint ? "oxlint" : devDeps.eslint ? "eslint" : devDeps["@biomejs/biome"] ? "biome" : void 0;
|
|
715
784
|
const formatter = devDeps.oxfmt ? "oxfmt" : devDeps.prettier ? "prettier" : devDeps["@biomejs/biome"] ? "biome" : void 0;
|
|
716
785
|
return { linter, formatter };
|
|
@@ -718,13 +787,33 @@ async function detectWorkspaceTooling(monorepoRoot) {
|
|
|
718
787
|
return {};
|
|
719
788
|
}
|
|
720
789
|
}
|
|
790
|
+
async function detectExistingConfigs(monorepoRoot) {
|
|
791
|
+
const configs = {};
|
|
792
|
+
const eslintPath = path.join(monorepoRoot, "eslint.config.js");
|
|
793
|
+
if (await fileExists(eslintPath)) {
|
|
794
|
+
configs.linter = "eslint";
|
|
795
|
+
configs.eslintConfigPath = eslintPath;
|
|
796
|
+
}
|
|
797
|
+
const prettierPath = path.join(monorepoRoot, ".prettierrc.json");
|
|
798
|
+
if (await fileExists(prettierPath)) {
|
|
799
|
+
configs.formatter = "prettier";
|
|
800
|
+
configs.prettierConfigPath = prettierPath;
|
|
801
|
+
}
|
|
802
|
+
const biomePath = path.join(monorepoRoot, "biome.json");
|
|
803
|
+
if (await fileExists(biomePath)) {
|
|
804
|
+
configs.biomeConfigPath = biomePath;
|
|
805
|
+
if (!configs.linter) configs.linter = "biome";
|
|
806
|
+
if (!configs.formatter) configs.formatter = "biome";
|
|
807
|
+
}
|
|
808
|
+
return configs;
|
|
809
|
+
}
|
|
721
810
|
async function getMonorepoScope(monorepoRoot) {
|
|
722
811
|
try {
|
|
723
812
|
const pkgPath = path.join(monorepoRoot, "package.json");
|
|
724
813
|
const content = await promises.readFile(pkgPath, "utf-8");
|
|
725
|
-
const
|
|
726
|
-
if (
|
|
727
|
-
return
|
|
814
|
+
const pkgJson = JSON.parse(content);
|
|
815
|
+
if (pkgJson.name) {
|
|
816
|
+
return pkgJson.name.replace(/^@/, "").replace(/\/.*$/, "");
|
|
728
817
|
}
|
|
729
818
|
} catch {
|
|
730
819
|
}
|
|
@@ -741,9 +830,12 @@ async function getWorkspacePackages(monorepoRoot) {
|
|
|
741
830
|
try {
|
|
742
831
|
const pkgJsonPath = path.join(packagesDir, entry.name, "package.json");
|
|
743
832
|
const content = await promises.readFile(pkgJsonPath, "utf-8");
|
|
744
|
-
const
|
|
745
|
-
if (
|
|
746
|
-
packages.push({
|
|
833
|
+
const pkgJson = JSON.parse(content);
|
|
834
|
+
if (pkgJson.name) {
|
|
835
|
+
packages.push({
|
|
836
|
+
name: pkgJson.name,
|
|
837
|
+
path: `packages/${entry.name}`
|
|
838
|
+
});
|
|
747
839
|
}
|
|
748
840
|
} catch {
|
|
749
841
|
}
|
|
@@ -753,35 +845,243 @@ async function getWorkspacePackages(monorepoRoot) {
|
|
|
753
845
|
}
|
|
754
846
|
return packages;
|
|
755
847
|
}
|
|
848
|
+
async function ensureConfigInWorkspace(monorepoRoot) {
|
|
849
|
+
const workspacePath = path.join(monorepoRoot, "pnpm-workspace.yaml");
|
|
850
|
+
let content;
|
|
851
|
+
try {
|
|
852
|
+
content = await promises.readFile(workspacePath, "utf-8");
|
|
853
|
+
} catch {
|
|
854
|
+
content = `packages:
|
|
855
|
+
- ".config/*"
|
|
856
|
+
- "packages/*"
|
|
857
|
+
`;
|
|
858
|
+
await promises.writeFile(workspacePath, content);
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
861
|
+
if (content.includes(".config/*") || content.includes('".config/*"')) {
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
const lines = content.split("\n");
|
|
865
|
+
const packagesIndex = lines.findIndex(
|
|
866
|
+
(line) => line.trim().startsWith("packages:")
|
|
867
|
+
);
|
|
868
|
+
if (packagesIndex === -1) {
|
|
869
|
+
content = `packages:
|
|
870
|
+
- ".config/*"
|
|
871
|
+
${content}`;
|
|
872
|
+
} else {
|
|
873
|
+
lines.splice(packagesIndex + 1, 0, ' - ".config/*"');
|
|
874
|
+
content = lines.join("\n");
|
|
875
|
+
}
|
|
876
|
+
await promises.writeFile(workspacePath, content);
|
|
877
|
+
}
|
|
878
|
+
async function migrateEslintConfig(monorepoRoot, files) {
|
|
879
|
+
const configBasePath = ".config/eslint";
|
|
880
|
+
const existingConfigPath = path.join(monorepoRoot, "eslint.config.js");
|
|
881
|
+
let existingContent;
|
|
882
|
+
try {
|
|
883
|
+
existingContent = await promises.readFile(existingConfigPath, "utf-8");
|
|
884
|
+
} catch {
|
|
885
|
+
index.generateEslintConfigPackage(files);
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
files[`${configBasePath}/package.json`] = {
|
|
889
|
+
type: "text",
|
|
890
|
+
content: JSON.stringify(
|
|
891
|
+
{
|
|
892
|
+
name: "@config/eslint",
|
|
893
|
+
version: "0.1.0",
|
|
894
|
+
private: true,
|
|
895
|
+
type: "module",
|
|
896
|
+
exports: {
|
|
897
|
+
"./base": "./base.js",
|
|
898
|
+
"./react": "./react.js"
|
|
899
|
+
}
|
|
900
|
+
},
|
|
901
|
+
null,
|
|
902
|
+
2
|
|
903
|
+
)
|
|
904
|
+
};
|
|
905
|
+
files[`${configBasePath}/README.md`] = {
|
|
906
|
+
type: "text",
|
|
907
|
+
content: `# \`@config/eslint\`
|
|
908
|
+
|
|
909
|
+
Shared ESLint configurations.
|
|
910
|
+
|
|
911
|
+
## Usage
|
|
912
|
+
|
|
913
|
+
In your package's \`eslint.config.js\`:
|
|
914
|
+
|
|
915
|
+
\`\`\`js
|
|
916
|
+
import base from "@config/eslint/base";
|
|
917
|
+
|
|
918
|
+
export default [...base];
|
|
919
|
+
\`\`\`
|
|
920
|
+
|
|
921
|
+
## Available Configs
|
|
922
|
+
|
|
923
|
+
- \`base\` - Base ESLint rules (migrated from root)
|
|
924
|
+
- \`react\` - React-specific rules
|
|
925
|
+
`
|
|
926
|
+
};
|
|
927
|
+
files[`${configBasePath}/base.js`] = {
|
|
928
|
+
type: "text",
|
|
929
|
+
content: existingContent
|
|
930
|
+
};
|
|
931
|
+
files[`${configBasePath}/react.js`] = {
|
|
932
|
+
type: "text",
|
|
933
|
+
content: `import react from "eslint-plugin-react";
|
|
934
|
+
import reactHooks from "eslint-plugin-react-hooks";
|
|
935
|
+
|
|
936
|
+
export default [
|
|
937
|
+
{
|
|
938
|
+
plugins: {
|
|
939
|
+
react,
|
|
940
|
+
"react-hooks": reactHooks,
|
|
941
|
+
},
|
|
942
|
+
rules: {
|
|
943
|
+
...react.configs.recommended.rules,
|
|
944
|
+
...reactHooks.configs.recommended.rules,
|
|
945
|
+
"react/react-in-jsx-scope": "off",
|
|
946
|
+
},
|
|
947
|
+
settings: {
|
|
948
|
+
react: {
|
|
949
|
+
version: "detect",
|
|
950
|
+
},
|
|
951
|
+
},
|
|
952
|
+
},
|
|
953
|
+
];
|
|
954
|
+
`
|
|
955
|
+
};
|
|
956
|
+
}
|
|
957
|
+
async function migratePrettierConfig(monorepoRoot, files) {
|
|
958
|
+
const configBasePath = ".config/prettier";
|
|
959
|
+
const existingConfigPath = path.join(monorepoRoot, ".prettierrc.json");
|
|
960
|
+
let existingContent;
|
|
961
|
+
try {
|
|
962
|
+
existingContent = await promises.readFile(existingConfigPath, "utf-8");
|
|
963
|
+
} catch {
|
|
964
|
+
index.generatePrettierConfigPackage(files);
|
|
965
|
+
return;
|
|
966
|
+
}
|
|
967
|
+
files[`${configBasePath}/package.json`] = {
|
|
968
|
+
type: "text",
|
|
969
|
+
content: JSON.stringify(
|
|
970
|
+
{
|
|
971
|
+
name: "@config/prettier",
|
|
972
|
+
version: "0.1.0",
|
|
973
|
+
private: true,
|
|
974
|
+
exports: {
|
|
975
|
+
"./base": "./base.json"
|
|
976
|
+
}
|
|
977
|
+
},
|
|
978
|
+
null,
|
|
979
|
+
2
|
|
980
|
+
)
|
|
981
|
+
};
|
|
982
|
+
files[`${configBasePath}/README.md`] = {
|
|
983
|
+
type: "text",
|
|
984
|
+
content: `# \`@config/prettier\`
|
|
985
|
+
|
|
986
|
+
Shared Prettier configurations.
|
|
987
|
+
|
|
988
|
+
## Usage
|
|
989
|
+
|
|
990
|
+
In your package's \`.prettierrc\`:
|
|
991
|
+
|
|
992
|
+
\`\`\`json
|
|
993
|
+
"@config/prettier/base"
|
|
994
|
+
\`\`\`
|
|
995
|
+
|
|
996
|
+
Or in \`package.json\`:
|
|
997
|
+
|
|
998
|
+
\`\`\`json
|
|
999
|
+
{
|
|
1000
|
+
"prettier": "@config/prettier/base"
|
|
1001
|
+
}
|
|
1002
|
+
\`\`\`
|
|
1003
|
+
|
|
1004
|
+
## Available Configs
|
|
1005
|
+
|
|
1006
|
+
- \`base\` - Base Prettier rules (migrated from root)
|
|
1007
|
+
`
|
|
1008
|
+
};
|
|
1009
|
+
files[`${configBasePath}/base.json`] = {
|
|
1010
|
+
type: "text",
|
|
1011
|
+
content: existingContent
|
|
1012
|
+
};
|
|
1013
|
+
}
|
|
756
1014
|
async function createPackageInWorkspace(monorepoRoot, packageManager, inheritedTooling, scope) {
|
|
1015
|
+
const workspaceDirectories = await parseWorkspaceDirectories(monorepoRoot);
|
|
1016
|
+
const defaultDirectories = ["apps", "packages"];
|
|
1017
|
+
const hasCustomDirectories = workspaceDirectories.length > 0 && !workspaceDirectories.every((dir) => defaultDirectories.includes(dir));
|
|
757
1018
|
const packageType = await promptForInitialPackage();
|
|
758
1019
|
if (packageType === "skip") {
|
|
759
1020
|
return false;
|
|
760
1021
|
}
|
|
1022
|
+
const defaultDir = packageType === "app" ? "apps" : "packages";
|
|
761
1023
|
const packageNameInput = await p__namespace.text({
|
|
762
1024
|
message: "Package name?",
|
|
763
|
-
|
|
1025
|
+
initialValue: `@${scope}/`,
|
|
764
1026
|
validate: (value) => {
|
|
765
|
-
|
|
1027
|
+
const validationError = index.validatePackageName(value);
|
|
1028
|
+
if (validationError) return validationError;
|
|
1029
|
+
const dirName = value.includes("/") ? value.split("/").pop() : value;
|
|
1030
|
+
if (!dirName) return "Package name is required";
|
|
1031
|
+
if (!hasCustomDirectories) {
|
|
1032
|
+
const targetPath = path.join(monorepoRoot, defaultDir, dirName);
|
|
1033
|
+
try {
|
|
1034
|
+
const { statSync } = require$1("fs");
|
|
1035
|
+
statSync(targetPath);
|
|
1036
|
+
return `Directory ${defaultDir}/${dirName} already exists`;
|
|
1037
|
+
} catch {
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
766
1040
|
}
|
|
767
1041
|
});
|
|
768
1042
|
if (p__namespace.isCancel(packageNameInput)) {
|
|
769
1043
|
return false;
|
|
770
1044
|
}
|
|
771
|
-
const
|
|
772
|
-
const
|
|
773
|
-
const targetDir = packageType === "app" ? "apps" : "packages";
|
|
774
|
-
const packagePath = path.join(targetDir, shortName);
|
|
775
|
-
const workspaceRoot = "../..";
|
|
1045
|
+
const scopedName = packageNameInput;
|
|
1046
|
+
const shortName = scopedName.includes("/") ? scopedName.split("/").pop() : scopedName;
|
|
776
1047
|
const packageOptions = await promptForPackageOptions(
|
|
777
1048
|
scopedName,
|
|
778
1049
|
packageType,
|
|
779
1050
|
inheritedTooling
|
|
780
1051
|
);
|
|
1052
|
+
let targetDir = defaultDir;
|
|
1053
|
+
if (hasCustomDirectories && workspaceDirectories.length > 0) {
|
|
1054
|
+
const dirChoice = await p__namespace.select({
|
|
1055
|
+
message: "Target directory",
|
|
1056
|
+
options: workspaceDirectories.map((dir) => ({
|
|
1057
|
+
value: dir,
|
|
1058
|
+
label: dir
|
|
1059
|
+
})),
|
|
1060
|
+
initialValue: workspaceDirectories.includes(defaultDir) ? defaultDir : workspaceDirectories[0]
|
|
1061
|
+
});
|
|
1062
|
+
if (p__namespace.isCancel(dirChoice)) {
|
|
1063
|
+
return false;
|
|
1064
|
+
}
|
|
1065
|
+
targetDir = dirChoice;
|
|
1066
|
+
const targetPath = path.join(monorepoRoot, targetDir, shortName);
|
|
1067
|
+
try {
|
|
1068
|
+
const { statSync } = require$1("fs");
|
|
1069
|
+
statSync(targetPath);
|
|
1070
|
+
p__namespace.log.error(`Directory ${targetDir}/${shortName} already exists`);
|
|
1071
|
+
return false;
|
|
1072
|
+
} catch {
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
const relativePkgPath = path.join(targetDir, shortName);
|
|
1076
|
+
const workspaceRoot = calculateWorkspaceRoot(relativePkgPath);
|
|
781
1077
|
packageOptions.workspaceRoot = workspaceRoot;
|
|
782
1078
|
packageOptions.name = scopedName;
|
|
783
1079
|
if (packageManager === "pnpm") {
|
|
784
1080
|
packageOptions.pnpmVersion = await index.getLatestPnpmVersion();
|
|
1081
|
+
} else if (packageManager === "yarn") {
|
|
1082
|
+
packageOptions.yarnVersion = await index.getLatestYarnVersion();
|
|
1083
|
+
} else if (packageManager === "npm") {
|
|
1084
|
+
packageOptions.npmVersion = await index.getLatestNpmCliVersion();
|
|
785
1085
|
}
|
|
786
1086
|
const nodeVersion = packageOptions.nodeVersion ?? "latest";
|
|
787
1087
|
if (nodeVersion === "latest") {
|
|
@@ -852,9 +1152,9 @@ async function createPackageInWorkspace(monorepoRoot, packageManager, inheritedT
|
|
|
852
1152
|
if (workspacePackages.length > 0) {
|
|
853
1153
|
const selectedDeps = await p__namespace.multiselect({
|
|
854
1154
|
message: "Add workspace dependencies?",
|
|
855
|
-
options: workspacePackages.map((
|
|
856
|
-
value:
|
|
857
|
-
label:
|
|
1155
|
+
options: workspacePackages.map((pkgInfo) => ({
|
|
1156
|
+
value: pkgInfo.name,
|
|
1157
|
+
label: pkgInfo.name.replace(/^@[^/]+\//, "")
|
|
858
1158
|
})),
|
|
859
1159
|
required: false
|
|
860
1160
|
});
|
|
@@ -863,24 +1163,15 @@ async function createPackageInWorkspace(monorepoRoot, packageManager, inheritedT
|
|
|
863
1163
|
}
|
|
864
1164
|
}
|
|
865
1165
|
}
|
|
866
|
-
const
|
|
867
|
-
const
|
|
868
|
-
|
|
1166
|
+
const outputPath = path.join(monorepoRoot, relativePkgPath);
|
|
1167
|
+
const spinner = p__namespace.spinner();
|
|
1168
|
+
spinner.start("Creating package...");
|
|
869
1169
|
try {
|
|
870
1170
|
const files = index.generate(packageOptions);
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
const file = files[filePath];
|
|
876
|
-
if (file.type === "text") {
|
|
877
|
-
await promises.writeFile(fullFilePath, file.content);
|
|
878
|
-
} else {
|
|
879
|
-
const response = await undici.fetch(file.url);
|
|
880
|
-
await promises.writeFile(fullFilePath, response.body);
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
s.stop(color__default.green.inverse(` \u2713 Package created at ${packagePath}! `));
|
|
1171
|
+
await writeGeneratedFiles(outputPath, files);
|
|
1172
|
+
spinner.stop(
|
|
1173
|
+
color__default.green.inverse(` \u2713 Package created at ${relativePkgPath}! `)
|
|
1174
|
+
);
|
|
884
1175
|
const addAnother = await p__namespace.select({
|
|
885
1176
|
message: "Add another package?",
|
|
886
1177
|
options: [
|
|
@@ -891,12 +1182,12 @@ async function createPackageInWorkspace(monorepoRoot, packageManager, inheritedT
|
|
|
891
1182
|
});
|
|
892
1183
|
return !p__namespace.isCancel(addAnother) && addAnother === "yes";
|
|
893
1184
|
} catch (error) {
|
|
894
|
-
|
|
1185
|
+
spinner.stop("Failed to create package");
|
|
895
1186
|
p__namespace.log.error(String(error));
|
|
896
1187
|
return false;
|
|
897
1188
|
}
|
|
898
1189
|
}
|
|
899
|
-
async function promptAndOpenEditor(
|
|
1190
|
+
async function promptAndOpenEditor(projectPath) {
|
|
900
1191
|
const savedEditor = getPreferredEditor();
|
|
901
1192
|
let selectedEditor;
|
|
902
1193
|
if (savedEditor && savedEditor !== "skip") {
|
|
@@ -946,7 +1237,7 @@ async function promptAndOpenEditor(basePath) {
|
|
|
946
1237
|
try {
|
|
947
1238
|
await openInEditor(
|
|
948
1239
|
selectedEditor,
|
|
949
|
-
|
|
1240
|
+
projectPath,
|
|
950
1241
|
getReuseWindow()
|
|
951
1242
|
);
|
|
952
1243
|
p__namespace.log.success(`Opening in ${editorNames[selectedEditor]}...`);
|
|
@@ -957,14 +1248,673 @@ async function promptAndOpenEditor(basePath) {
|
|
|
957
1248
|
}
|
|
958
1249
|
}
|
|
959
1250
|
}
|
|
1251
|
+
async function handleCheckCommand() {
|
|
1252
|
+
const monorepoRoot = await detectMonorepoRoot();
|
|
1253
|
+
if (!monorepoRoot) {
|
|
1254
|
+
console.log(color__default.red("\u2717") + " Not a monorepo workspace");
|
|
1255
|
+
process.exit(1);
|
|
1256
|
+
}
|
|
1257
|
+
const { valid, errors } = await validateWorkspace(monorepoRoot);
|
|
1258
|
+
if (valid) {
|
|
1259
|
+
console.log(color__default.green("\u2713") + " Valid monorepo workspace");
|
|
1260
|
+
console.log(color__default.dim(` ${monorepoRoot}`));
|
|
1261
|
+
} else {
|
|
1262
|
+
console.log(color__default.red("\u2717") + " Invalid monorepo workspace");
|
|
1263
|
+
console.log(color__default.dim(` ${monorepoRoot}`));
|
|
1264
|
+
for (const error of errors) {
|
|
1265
|
+
console.log(color__default.red(` \u2022 ${error}`));
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
process.exit(valid ? 0 : 1);
|
|
1269
|
+
}
|
|
1270
|
+
async function handleFixCommand(options) {
|
|
1271
|
+
const monorepoRoot = await detectMonorepoRoot();
|
|
1272
|
+
if (!monorepoRoot) {
|
|
1273
|
+
console.log(color__default.red("\u2717") + " Not a monorepo workspace");
|
|
1274
|
+
console.log(color__default.dim(" Run this command from within a monorepo"));
|
|
1275
|
+
process.exit(1);
|
|
1276
|
+
}
|
|
1277
|
+
const { valid, errors } = await validateWorkspace(monorepoRoot);
|
|
1278
|
+
if (valid) {
|
|
1279
|
+
console.log(color__default.green("\u2713") + " Workspace is already valid");
|
|
1280
|
+
console.log(color__default.dim(` ${monorepoRoot}`));
|
|
1281
|
+
process.exit(0);
|
|
1282
|
+
}
|
|
1283
|
+
console.log(color__default.yellow("!") + " Invalid monorepo workspace");
|
|
1284
|
+
for (const error of errors) {
|
|
1285
|
+
console.log(color__default.dim(` \u2022 ${error}`));
|
|
1286
|
+
}
|
|
1287
|
+
console.log();
|
|
1288
|
+
const tooling = await detectWorkspaceTooling(monorepoRoot);
|
|
1289
|
+
const existingConfigs = await detectExistingConfigs(monorepoRoot);
|
|
1290
|
+
const detectedLinter = tooling.linter ?? existingConfigs.linter ?? "oxlint";
|
|
1291
|
+
const detectedFormatter = tooling.formatter ?? existingConfigs.formatter ?? "oxfmt";
|
|
1292
|
+
const isNonInteractive = options.linter && options.formatter;
|
|
1293
|
+
let linter;
|
|
1294
|
+
let formatter;
|
|
1295
|
+
if (isNonInteractive) {
|
|
1296
|
+
linter = options.linter;
|
|
1297
|
+
formatter = options.formatter;
|
|
1298
|
+
} else {
|
|
1299
|
+
const linterChoice = await p__namespace.select({
|
|
1300
|
+
message: "Linter",
|
|
1301
|
+
options: [
|
|
1302
|
+
{
|
|
1303
|
+
value: "oxlint",
|
|
1304
|
+
label: "oxlint" + (tooling.linter === "oxlint" ? color__default.dim(" (installed)") : "")
|
|
1305
|
+
},
|
|
1306
|
+
{
|
|
1307
|
+
value: "eslint",
|
|
1308
|
+
label: "eslint" + (tooling.linter === "eslint" || existingConfigs.linter === "eslint" ? color__default.dim(" (installed)") : "")
|
|
1309
|
+
},
|
|
1310
|
+
{
|
|
1311
|
+
value: "biome",
|
|
1312
|
+
label: "biome" + (tooling.linter === "biome" ? color__default.dim(" (installed)") : "")
|
|
1313
|
+
}
|
|
1314
|
+
],
|
|
1315
|
+
initialValue: detectedLinter
|
|
1316
|
+
});
|
|
1317
|
+
if (p__namespace.isCancel(linterChoice)) {
|
|
1318
|
+
p__namespace.cancel("Operation cancelled.");
|
|
1319
|
+
process.exit(0);
|
|
1320
|
+
}
|
|
1321
|
+
const formatterChoice = await p__namespace.select({
|
|
1322
|
+
message: "Formatter",
|
|
1323
|
+
options: [
|
|
1324
|
+
{
|
|
1325
|
+
value: "oxfmt",
|
|
1326
|
+
label: "oxfmt" + (tooling.formatter === "oxfmt" ? color__default.dim(" (installed)") : "")
|
|
1327
|
+
},
|
|
1328
|
+
{
|
|
1329
|
+
value: "prettier",
|
|
1330
|
+
label: "prettier" + (tooling.formatter === "prettier" || existingConfigs.formatter === "prettier" ? color__default.dim(" (installed)") : "")
|
|
1331
|
+
},
|
|
1332
|
+
{
|
|
1333
|
+
value: "biome",
|
|
1334
|
+
label: "biome" + (tooling.formatter === "biome" ? color__default.dim(" (installed)") : "")
|
|
1335
|
+
}
|
|
1336
|
+
],
|
|
1337
|
+
initialValue: detectedFormatter
|
|
1338
|
+
});
|
|
1339
|
+
if (p__namespace.isCancel(formatterChoice)) {
|
|
1340
|
+
p__namespace.cancel("Operation cancelled.");
|
|
1341
|
+
process.exit(0);
|
|
1342
|
+
}
|
|
1343
|
+
linter = linterChoice;
|
|
1344
|
+
formatter = formatterChoice;
|
|
1345
|
+
}
|
|
1346
|
+
console.log();
|
|
1347
|
+
const spinner = p__namespace.spinner();
|
|
1348
|
+
spinner.start("Fixing workspace...");
|
|
1349
|
+
try {
|
|
1350
|
+
const files = {};
|
|
1351
|
+
const tsConfigExists = await fileExists(
|
|
1352
|
+
path.join(monorepoRoot, ".config/typescript/package.json")
|
|
1353
|
+
);
|
|
1354
|
+
if (!tsConfigExists) {
|
|
1355
|
+
index.generateTypescriptConfigPackage(files);
|
|
1356
|
+
}
|
|
1357
|
+
if (linter === "oxlint") {
|
|
1358
|
+
const oxlintExists = await fileExists(
|
|
1359
|
+
path.join(monorepoRoot, ".config/oxlint/package.json")
|
|
1360
|
+
);
|
|
1361
|
+
if (!oxlintExists) index.generateOxlintConfigPackage(files);
|
|
1362
|
+
} else if (linter === "eslint") {
|
|
1363
|
+
const eslintPkgExists = await fileExists(
|
|
1364
|
+
path.join(monorepoRoot, ".config/eslint/package.json")
|
|
1365
|
+
);
|
|
1366
|
+
if (!eslintPkgExists) {
|
|
1367
|
+
if (existingConfigs.eslintConfigPath) {
|
|
1368
|
+
await migrateEslintConfig(monorepoRoot, files);
|
|
1369
|
+
} else {
|
|
1370
|
+
index.generateEslintConfigPackage(files);
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
if (formatter === "oxfmt") {
|
|
1375
|
+
const oxfmtExists = await fileExists(
|
|
1376
|
+
path.join(monorepoRoot, ".config/oxfmt/package.json")
|
|
1377
|
+
);
|
|
1378
|
+
if (!oxfmtExists) index.generateOxfmtConfigPackage(files);
|
|
1379
|
+
} else if (formatter === "prettier") {
|
|
1380
|
+
const prettierPkgExists = await fileExists(
|
|
1381
|
+
path.join(monorepoRoot, ".config/prettier/package.json")
|
|
1382
|
+
);
|
|
1383
|
+
if (!prettierPkgExists) {
|
|
1384
|
+
if (existingConfigs.prettierConfigPath) {
|
|
1385
|
+
await migratePrettierConfig(monorepoRoot, files);
|
|
1386
|
+
} else {
|
|
1387
|
+
index.generatePrettierConfigPackage(files);
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
if ((linter === "biome" || formatter === "biome") && !existingConfigs.biomeConfigPath) {
|
|
1392
|
+
const biomeConfig = {
|
|
1393
|
+
$schema: "https://biomejs.dev/schemas/1.9.4/schema.json",
|
|
1394
|
+
vcs: {
|
|
1395
|
+
enabled: true,
|
|
1396
|
+
clientKind: "git",
|
|
1397
|
+
useIgnoreFile: true
|
|
1398
|
+
},
|
|
1399
|
+
linter: {
|
|
1400
|
+
enabled: linter === "biome",
|
|
1401
|
+
rules: {
|
|
1402
|
+
recommended: true
|
|
1403
|
+
}
|
|
1404
|
+
},
|
|
1405
|
+
formatter: {
|
|
1406
|
+
enabled: formatter === "biome"
|
|
1407
|
+
}
|
|
1408
|
+
};
|
|
1409
|
+
files["biome.json"] = {
|
|
1410
|
+
type: "text",
|
|
1411
|
+
content: JSON.stringify(biomeConfig, null, 2)
|
|
1412
|
+
};
|
|
1413
|
+
}
|
|
1414
|
+
for (const [filePath, file] of Object.entries(files)) {
|
|
1415
|
+
const fullPath = path.join(monorepoRoot, filePath);
|
|
1416
|
+
await promises.mkdir(path.dirname(fullPath), { recursive: true });
|
|
1417
|
+
await promises.writeFile(fullPath, file.content);
|
|
1418
|
+
}
|
|
1419
|
+
await ensureConfigInWorkspace(monorepoRoot);
|
|
1420
|
+
if (existingConfigs.eslintConfigPath && linter === "eslint") {
|
|
1421
|
+
try {
|
|
1422
|
+
await promises.unlink(existingConfigs.eslintConfigPath);
|
|
1423
|
+
} catch {
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
if (existingConfigs.prettierConfigPath && formatter === "prettier") {
|
|
1427
|
+
try {
|
|
1428
|
+
await promises.unlink(existingConfigs.prettierConfigPath);
|
|
1429
|
+
} catch {
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
spinner.stop(color__default.green("\u2713") + " Workspace fixed!");
|
|
1433
|
+
const generated = Object.keys(files).filter(
|
|
1434
|
+
(f) => f.endsWith("package.json")
|
|
1435
|
+
);
|
|
1436
|
+
for (const pkgFile of generated) {
|
|
1437
|
+
const pkgName = pkgFile.replace("/package.json", "");
|
|
1438
|
+
console.log(color__default.dim(` Generated ${pkgName}`));
|
|
1439
|
+
}
|
|
1440
|
+
const vscodeSettingsExists = await fileExists(
|
|
1441
|
+
path.join(monorepoRoot, ".vscode/settings.json")
|
|
1442
|
+
);
|
|
1443
|
+
const vscodeExtensionsExists = await fileExists(
|
|
1444
|
+
path.join(monorepoRoot, ".vscode/extensions.json")
|
|
1445
|
+
);
|
|
1446
|
+
const vscodeExists = vscodeSettingsExists && vscodeExtensionsExists;
|
|
1447
|
+
if (!vscodeExists) {
|
|
1448
|
+
let addVscode = false;
|
|
1449
|
+
if (isNonInteractive) {
|
|
1450
|
+
addVscode = true;
|
|
1451
|
+
} else {
|
|
1452
|
+
const vscodeChoice = await p__namespace.confirm({
|
|
1453
|
+
message: "Generate VS Code settings?",
|
|
1454
|
+
initialValue: true
|
|
1455
|
+
});
|
|
1456
|
+
addVscode = !p__namespace.isCancel(vscodeChoice) && vscodeChoice;
|
|
1457
|
+
}
|
|
1458
|
+
if (addVscode) {
|
|
1459
|
+
const vscodeFiles = {};
|
|
1460
|
+
index.generateVscodeFiles(vscodeFiles, linter, formatter);
|
|
1461
|
+
for (const [filePath, file] of Object.entries(vscodeFiles)) {
|
|
1462
|
+
const fullPath = path.join(monorepoRoot, filePath);
|
|
1463
|
+
await promises.mkdir(path.dirname(fullPath), { recursive: true });
|
|
1464
|
+
await promises.writeFile(fullPath, file.content);
|
|
1465
|
+
}
|
|
1466
|
+
console.log(color__default.dim(" Generated .vscode/settings.json"));
|
|
1467
|
+
console.log(color__default.dim(" Generated .vscode/extensions.json"));
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
const aiFilePaths = {
|
|
1471
|
+
"cursor-rules": ".cursor/rules",
|
|
1472
|
+
"agents-md": "AGENTS.md",
|
|
1473
|
+
"claude-md": "CLAUDE.md",
|
|
1474
|
+
"copilot-md": ".github/copilot-instructions.md"
|
|
1475
|
+
};
|
|
1476
|
+
const existingAiFiles = [];
|
|
1477
|
+
for (const [choice, path$1] of Object.entries(aiFilePaths)) {
|
|
1478
|
+
if (await fileExists(path.join(monorepoRoot, path$1))) {
|
|
1479
|
+
existingAiFiles.push(choice);
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
let selectedAiFiles = [];
|
|
1483
|
+
const savedAiFiles = getAiFiles();
|
|
1484
|
+
const availableChoices = ["cursor-rules", "agents-md", "claude-md", "copilot-md"].filter((c) => !existingAiFiles.includes(c));
|
|
1485
|
+
if (availableChoices.length === 0) {
|
|
1486
|
+
} else if (isNonInteractive) {
|
|
1487
|
+
const preferred = savedAiFiles ?? ["cursor-rules"];
|
|
1488
|
+
selectedAiFiles = preferred.filter((f) => availableChoices.includes(f));
|
|
1489
|
+
} else if (savedAiFiles && savedAiFiles.length > 0) {
|
|
1490
|
+
const availableSaved = savedAiFiles.filter(
|
|
1491
|
+
(f) => availableChoices.includes(f)
|
|
1492
|
+
);
|
|
1493
|
+
if (availableSaved.length > 0) {
|
|
1494
|
+
const savedLabels = availableSaved.map((f) => aiFilePaths[f]).join(", ");
|
|
1495
|
+
const useDefault = await p__namespace.confirm({
|
|
1496
|
+
message: `Generate AI instruction files? ${color__default.dim(
|
|
1497
|
+
`(${savedLabels})`
|
|
1498
|
+
)}`,
|
|
1499
|
+
initialValue: true
|
|
1500
|
+
});
|
|
1501
|
+
if (!p__namespace.isCancel(useDefault) && useDefault) {
|
|
1502
|
+
selectedAiFiles = availableSaved;
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
} else {
|
|
1506
|
+
const aiFilesChoice = await p__namespace.multiselect({
|
|
1507
|
+
message: "Generate AI instruction files?",
|
|
1508
|
+
options: availableChoices.map((c) => ({
|
|
1509
|
+
value: c,
|
|
1510
|
+
label: aiFilePaths[c],
|
|
1511
|
+
hint: c === "cursor-rules" ? "Cursor AI" : c === "agents-md" ? "GitHub Copilot, general" : c === "claude-md" ? "Claude" : "GitHub Copilot"
|
|
1512
|
+
})),
|
|
1513
|
+
required: false
|
|
1514
|
+
});
|
|
1515
|
+
if (!p__namespace.isCancel(aiFilesChoice) && aiFilesChoice.length > 0) {
|
|
1516
|
+
selectedAiFiles = aiFilesChoice;
|
|
1517
|
+
const saveChoice = await p__namespace.confirm({
|
|
1518
|
+
message: "Save as default for future?",
|
|
1519
|
+
initialValue: true
|
|
1520
|
+
});
|
|
1521
|
+
if (!p__namespace.isCancel(saveChoice) && saveChoice) {
|
|
1522
|
+
setAiFiles(selectedAiFiles);
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
if (selectedAiFiles.length > 0) {
|
|
1527
|
+
const scope = await getMonorepoScope(monorepoRoot);
|
|
1528
|
+
const aiFilesOutput = {};
|
|
1529
|
+
index.generateAiFiles(aiFilesOutput, {
|
|
1530
|
+
name: scope,
|
|
1531
|
+
packageManager: "pnpm",
|
|
1532
|
+
linter,
|
|
1533
|
+
formatter,
|
|
1534
|
+
aiFiles: selectedAiFiles
|
|
1535
|
+
});
|
|
1536
|
+
for (const [filePath, file] of Object.entries(aiFilesOutput)) {
|
|
1537
|
+
const fullPath = path.join(monorepoRoot, filePath);
|
|
1538
|
+
await promises.mkdir(path.dirname(fullPath), { recursive: true });
|
|
1539
|
+
await promises.writeFile(fullPath, file.content);
|
|
1540
|
+
console.log(color__default.dim(` Generated ${filePath}`));
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
process.exit(0);
|
|
1544
|
+
} catch (error) {
|
|
1545
|
+
spinner.stop(color__default.red("\u2717") + " Failed to fix workspace");
|
|
1546
|
+
console.error(error);
|
|
1547
|
+
process.exit(1);
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
async function handleWorkspaceCommand(name, options) {
|
|
1551
|
+
const monorepoRoot = await detectMonorepoRoot();
|
|
1552
|
+
if (!monorepoRoot) {
|
|
1553
|
+
console.error(
|
|
1554
|
+
color__default.red("Error:") + " --workspace flag requires being inside a monorepo"
|
|
1555
|
+
);
|
|
1556
|
+
process.exit(1);
|
|
1557
|
+
}
|
|
1558
|
+
if (!name) {
|
|
1559
|
+
console.error(
|
|
1560
|
+
color__default.red("Error:") + " Package name is required with --workspace flag"
|
|
1561
|
+
);
|
|
1562
|
+
console.log(
|
|
1563
|
+
color__default.dim(
|
|
1564
|
+
" Example: pnpm create krispya my-lib --workspace --type library"
|
|
1565
|
+
)
|
|
1566
|
+
);
|
|
1567
|
+
process.exit(1);
|
|
1568
|
+
}
|
|
1569
|
+
const scope = await getMonorepoScope(monorepoRoot);
|
|
1570
|
+
const inheritedTooling = await detectWorkspaceTooling(monorepoRoot);
|
|
1571
|
+
const projectType = options.type ?? "app";
|
|
1572
|
+
const defaultDir = projectType === "library" ? "packages" : "apps";
|
|
1573
|
+
const targetDir = options.dir ?? defaultDir;
|
|
1574
|
+
const template = options.template ?? "vanilla";
|
|
1575
|
+
const baseTemplate = index.getBaseTemplate(template);
|
|
1576
|
+
const scopedName = name.startsWith("@") ? name : `@${scope}/${name}`;
|
|
1577
|
+
const fullPackagePath = path.join(monorepoRoot, targetDir, name);
|
|
1578
|
+
try {
|
|
1579
|
+
await promises.access(fullPackagePath, fs.constants.F_OK);
|
|
1580
|
+
console.error(
|
|
1581
|
+
color__default.red("Error:") + ` Directory ${targetDir}/${name} already exists`
|
|
1582
|
+
);
|
|
1583
|
+
process.exit(1);
|
|
1584
|
+
} catch {
|
|
1585
|
+
}
|
|
1586
|
+
const versions = {};
|
|
1587
|
+
const versionPromises = [];
|
|
1588
|
+
const isLibrary = projectType === "library";
|
|
1589
|
+
if (!isLibrary) {
|
|
1590
|
+
versionPromises.push(
|
|
1591
|
+
index.getLatestNpmVersion("vite", "6.3.4").then((v) => {
|
|
1592
|
+
versions.vite = v;
|
|
1593
|
+
})
|
|
1594
|
+
);
|
|
1595
|
+
}
|
|
1596
|
+
const linter = inheritedTooling.linter ?? options.linter ?? "oxlint";
|
|
1597
|
+
const formatter = inheritedTooling.formatter ?? options.formatter ?? "oxfmt";
|
|
1598
|
+
await Promise.all(versionPromises);
|
|
1599
|
+
const relativePkgPath = path.join(targetDir, name);
|
|
1600
|
+
const workspaceRoot = calculateWorkspaceRoot(relativePkgPath);
|
|
1601
|
+
const generateOptions = {
|
|
1602
|
+
name: scopedName,
|
|
1603
|
+
projectType,
|
|
1604
|
+
libraryBundler: isLibrary ? options.bundler ?? "unbuild" : void 0,
|
|
1605
|
+
template,
|
|
1606
|
+
linter,
|
|
1607
|
+
formatter,
|
|
1608
|
+
workspaceRoot,
|
|
1609
|
+
versions,
|
|
1610
|
+
...baseTemplate === "r3f" && {
|
|
1611
|
+
drei: options.drei ? {} : void 0,
|
|
1612
|
+
handle: options.handle ? {} : void 0,
|
|
1613
|
+
leva: options.leva ? {} : void 0,
|
|
1614
|
+
postprocessing: options.postprocessing ? {} : void 0,
|
|
1615
|
+
rapier: options.rapier ? {} : void 0,
|
|
1616
|
+
xr: options.xr ? {} : void 0,
|
|
1617
|
+
uikit: options.uikit ? {} : void 0,
|
|
1618
|
+
offscreen: options.offscreen ? {} : void 0,
|
|
1619
|
+
zustand: options.zustand ? {} : void 0,
|
|
1620
|
+
koota: options.koota ? {} : void 0,
|
|
1621
|
+
viverse: options.viverse ? {} : void 0,
|
|
1622
|
+
triplex: options.triplex ? {} : void 0
|
|
1623
|
+
}
|
|
1624
|
+
};
|
|
1625
|
+
console.log(
|
|
1626
|
+
color__default.cyan("Creating") + ` ${scopedName} in ${targetDir}/${name}...`
|
|
1627
|
+
);
|
|
1628
|
+
try {
|
|
1629
|
+
const files = index.generate(generateOptions);
|
|
1630
|
+
await writeGeneratedFiles(fullPackagePath, files);
|
|
1631
|
+
console.log(
|
|
1632
|
+
color__default.green("\u2713") + ` Created ${scopedName} at ${targetDir}/${name}`
|
|
1633
|
+
);
|
|
1634
|
+
process.exit(0);
|
|
1635
|
+
} catch (error) {
|
|
1636
|
+
console.error(color__default.red("Error:") + " Failed to create package");
|
|
1637
|
+
console.error(String(error));
|
|
1638
|
+
process.exit(1);
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
async function handleMonorepoCreation(generateOptions) {
|
|
1642
|
+
const { generateMonorepo } = await import('./chunks/index.cjs').then(function (n) { return n.monorepo; });
|
|
1643
|
+
const packageManager = generateOptions.packageManager || "pnpm";
|
|
1644
|
+
if (packageManager === "pnpm") {
|
|
1645
|
+
generateOptions.pnpmVersion = await index.getLatestPnpmVersion();
|
|
1646
|
+
} else if (packageManager === "yarn") {
|
|
1647
|
+
generateOptions.yarnVersion = await index.getLatestYarnVersion();
|
|
1648
|
+
} else if (packageManager === "npm") {
|
|
1649
|
+
generateOptions.npmVersion = await index.getLatestNpmCliVersion();
|
|
1650
|
+
}
|
|
1651
|
+
const nodeVersion = generateOptions.nodeVersion ?? "latest";
|
|
1652
|
+
if (nodeVersion === "latest") {
|
|
1653
|
+
generateOptions.nodeVersion = await index.getLatestNodeVersion();
|
|
1654
|
+
}
|
|
1655
|
+
const savedAiFiles = getAiFiles();
|
|
1656
|
+
let selectedAiFiles = [];
|
|
1657
|
+
if (savedAiFiles && savedAiFiles.length > 0) {
|
|
1658
|
+
const aiFileLabels = {
|
|
1659
|
+
"cursor-rules": ".cursor/rules",
|
|
1660
|
+
"agents-md": "AGENTS.md",
|
|
1661
|
+
"claude-md": "CLAUDE.md",
|
|
1662
|
+
"copilot-md": ".github/copilot-instructions.md"
|
|
1663
|
+
};
|
|
1664
|
+
const savedLabels = savedAiFiles.map((f) => aiFileLabels[f]).join(", ");
|
|
1665
|
+
const useDefault = await p__namespace.confirm({
|
|
1666
|
+
message: `Generate AI instruction files? ${color__default.dim(
|
|
1667
|
+
`(${savedLabels})`
|
|
1668
|
+
)}`,
|
|
1669
|
+
initialValue: true
|
|
1670
|
+
});
|
|
1671
|
+
if (!p__namespace.isCancel(useDefault) && useDefault) {
|
|
1672
|
+
selectedAiFiles = savedAiFiles;
|
|
1673
|
+
}
|
|
1674
|
+
} else {
|
|
1675
|
+
const aiFilesChoice = await p__namespace.multiselect({
|
|
1676
|
+
message: "Generate AI instruction files?",
|
|
1677
|
+
options: [
|
|
1678
|
+
{ value: "cursor-rules", label: ".cursor/rules", hint: "Cursor AI" },
|
|
1679
|
+
{
|
|
1680
|
+
value: "agents-md",
|
|
1681
|
+
label: "AGENTS.md",
|
|
1682
|
+
hint: "GitHub Copilot, general"
|
|
1683
|
+
},
|
|
1684
|
+
{ value: "claude-md", label: "CLAUDE.md", hint: "Claude" },
|
|
1685
|
+
{
|
|
1686
|
+
value: "copilot-md",
|
|
1687
|
+
label: ".github/copilot-instructions.md",
|
|
1688
|
+
hint: "GitHub Copilot"
|
|
1689
|
+
}
|
|
1690
|
+
],
|
|
1691
|
+
required: false
|
|
1692
|
+
});
|
|
1693
|
+
if (!p__namespace.isCancel(aiFilesChoice) && aiFilesChoice.length > 0) {
|
|
1694
|
+
selectedAiFiles = aiFilesChoice;
|
|
1695
|
+
const saveChoice = await p__namespace.confirm({
|
|
1696
|
+
message: "Save as default for future monorepos?",
|
|
1697
|
+
initialValue: true
|
|
1698
|
+
});
|
|
1699
|
+
if (!p__namespace.isCancel(saveChoice) && saveChoice) {
|
|
1700
|
+
setAiFiles(selectedAiFiles);
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
const projectPath = path.join(process$1.cwd(), generateOptions.name);
|
|
1705
|
+
const spinner = p__namespace.spinner();
|
|
1706
|
+
spinner.start("Creating monorepo workspace...");
|
|
1707
|
+
try {
|
|
1708
|
+
const { files } = generateMonorepo({
|
|
1709
|
+
name: generateOptions.name,
|
|
1710
|
+
linter: generateOptions.linter ?? "oxlint",
|
|
1711
|
+
formatter: generateOptions.formatter ?? "oxfmt",
|
|
1712
|
+
packageManager,
|
|
1713
|
+
pnpmVersion: generateOptions.pnpmVersion,
|
|
1714
|
+
pnpmManageVersions: generateOptions.pnpmManageVersions,
|
|
1715
|
+
nodeVersion: generateOptions.nodeVersion,
|
|
1716
|
+
aiFiles: selectedAiFiles.length > 0 ? selectedAiFiles : void 0
|
|
1717
|
+
});
|
|
1718
|
+
const filePaths = Object.keys(files).sort();
|
|
1719
|
+
for (const filePath of filePaths) {
|
|
1720
|
+
const fullFilePath = path.join(projectPath, filePath);
|
|
1721
|
+
await promises.mkdir(path.dirname(fullFilePath), { recursive: true });
|
|
1722
|
+
const file = files[filePath];
|
|
1723
|
+
if (file.type === "text") {
|
|
1724
|
+
await promises.writeFile(fullFilePath, file.content);
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
spinner.stop(color__default.green.inverse(" \u2713 Monorepo workspace created! "));
|
|
1728
|
+
const newMonorepoTooling = {
|
|
1729
|
+
linter: generateOptions.linter,
|
|
1730
|
+
formatter: generateOptions.formatter
|
|
1731
|
+
};
|
|
1732
|
+
const scope = generateOptions.name;
|
|
1733
|
+
let addMore = true;
|
|
1734
|
+
while (addMore) {
|
|
1735
|
+
addMore = await createPackageInWorkspace(
|
|
1736
|
+
projectPath,
|
|
1737
|
+
packageManager,
|
|
1738
|
+
newMonorepoTooling,
|
|
1739
|
+
scope
|
|
1740
|
+
);
|
|
1741
|
+
}
|
|
1742
|
+
const nextSteps = [
|
|
1743
|
+
`cd ${generateOptions.name}`,
|
|
1744
|
+
`${packageManager} install`,
|
|
1745
|
+
`${packageManager} run dev`
|
|
1746
|
+
].join("\n");
|
|
1747
|
+
p__namespace.note(nextSteps, "Next steps");
|
|
1748
|
+
await promptAndOpenEditor(projectPath);
|
|
1749
|
+
p__namespace.outro(color__default.green("Happy coding! \u2728"));
|
|
1750
|
+
process.exit(0);
|
|
1751
|
+
} catch (error) {
|
|
1752
|
+
spinner.stop("Failed to create monorepo workspace");
|
|
1753
|
+
p__namespace.log.error(String(error));
|
|
1754
|
+
process.exit(1);
|
|
1755
|
+
}
|
|
1756
|
+
}
|
|
1757
|
+
async function handleStandaloneProjectCreation(generateOptions) {
|
|
1758
|
+
const base = generateOptions.template ? index.getBaseTemplate(generateOptions.template) : "vanilla";
|
|
1759
|
+
const defaultFallbackName = base === "vanilla" ? "vanilla-app" : base === "react" ? "react-app" : "react-three-app";
|
|
1760
|
+
generateOptions.name ??= defaultFallbackName;
|
|
1761
|
+
const packageManager = generateOptions.packageManager || "pnpm";
|
|
1762
|
+
if (packageManager === "pnpm") {
|
|
1763
|
+
generateOptions.pnpmVersion = await index.getLatestPnpmVersion();
|
|
1764
|
+
} else if (packageManager === "yarn") {
|
|
1765
|
+
generateOptions.yarnVersion = await index.getLatestYarnVersion();
|
|
1766
|
+
} else if (packageManager === "npm") {
|
|
1767
|
+
generateOptions.npmVersion = await index.getLatestNpmCliVersion();
|
|
1768
|
+
}
|
|
1769
|
+
const nodeVersion = generateOptions.nodeVersion ?? "latest";
|
|
1770
|
+
if (nodeVersion === "latest") {
|
|
1771
|
+
generateOptions.nodeVersion = await index.getLatestNodeVersion();
|
|
1772
|
+
}
|
|
1773
|
+
const versions = {};
|
|
1774
|
+
const versionPromises = [];
|
|
1775
|
+
const isLibrary = generateOptions.projectType === "library";
|
|
1776
|
+
const testing = generateOptions.testing ?? (isLibrary ? "vitest" : "none");
|
|
1777
|
+
if (testing === "vitest") {
|
|
1778
|
+
versionPromises.push(
|
|
1779
|
+
index.getLatestNpmVersion("vitest", "4.0.0").then((v) => {
|
|
1780
|
+
versions.vitest = v;
|
|
1781
|
+
})
|
|
1782
|
+
);
|
|
1783
|
+
}
|
|
1784
|
+
if (!isLibrary) {
|
|
1785
|
+
versionPromises.push(
|
|
1786
|
+
index.getLatestNpmVersion("vite", "6.3.4").then((v) => {
|
|
1787
|
+
versions.vite = v;
|
|
1788
|
+
})
|
|
1789
|
+
);
|
|
1790
|
+
}
|
|
1791
|
+
const linter = generateOptions.linter ?? "oxlint";
|
|
1792
|
+
if (linter === "eslint") {
|
|
1793
|
+
versionPromises.push(
|
|
1794
|
+
index.getLatestNpmVersion("eslint", "9.17.0").then((v) => {
|
|
1795
|
+
versions.eslint = v;
|
|
1796
|
+
})
|
|
1797
|
+
);
|
|
1798
|
+
} else if (linter === "oxlint") {
|
|
1799
|
+
versionPromises.push(
|
|
1800
|
+
index.getLatestNpmVersion("oxlint", "0.16.0").then((v) => {
|
|
1801
|
+
versions.oxlint = v;
|
|
1802
|
+
})
|
|
1803
|
+
);
|
|
1804
|
+
} else if (linter === "biome") {
|
|
1805
|
+
versionPromises.push(
|
|
1806
|
+
index.getLatestNpmVersion("@biomejs/biome", "1.9.4").then((v) => {
|
|
1807
|
+
versions.biome = v;
|
|
1808
|
+
})
|
|
1809
|
+
);
|
|
1810
|
+
}
|
|
1811
|
+
const formatter = generateOptions.formatter ?? "oxfmt";
|
|
1812
|
+
if (formatter === "prettier") {
|
|
1813
|
+
versionPromises.push(
|
|
1814
|
+
index.getLatestNpmVersion("prettier", "3.4.2").then((v) => {
|
|
1815
|
+
versions.prettier = v;
|
|
1816
|
+
})
|
|
1817
|
+
);
|
|
1818
|
+
} else if (formatter === "oxfmt") {
|
|
1819
|
+
versionPromises.push(
|
|
1820
|
+
index.getLatestNpmVersion("oxfmt", "0.1.0").then((v) => {
|
|
1821
|
+
versions.oxfmt = v;
|
|
1822
|
+
})
|
|
1823
|
+
);
|
|
1824
|
+
} else if (formatter === "biome" && linter !== "biome") {
|
|
1825
|
+
versionPromises.push(
|
|
1826
|
+
index.getLatestNpmVersion("@biomejs/biome", "1.9.4").then((v) => {
|
|
1827
|
+
versions.biome = v;
|
|
1828
|
+
})
|
|
1829
|
+
);
|
|
1830
|
+
}
|
|
1831
|
+
await Promise.all(versionPromises);
|
|
1832
|
+
generateOptions.versions = versions;
|
|
1833
|
+
const projectPath = path.join(process$1.cwd(), generateOptions.name);
|
|
1834
|
+
const spinner = p__namespace.spinner();
|
|
1835
|
+
spinner.start("Creating project...");
|
|
1836
|
+
try {
|
|
1837
|
+
const files = index.generate(generateOptions);
|
|
1838
|
+
await writeGeneratedFiles(projectPath, files);
|
|
1839
|
+
spinner.stop(color__default.green.inverse(" \u2713 Project created! "));
|
|
1840
|
+
const nextSteps = isLibrary ? [
|
|
1841
|
+
`cd ${generateOptions.name}`,
|
|
1842
|
+
`${packageManager} install`,
|
|
1843
|
+
`${packageManager} run build`
|
|
1844
|
+
].join("\n") : [
|
|
1845
|
+
`cd ${generateOptions.name}`,
|
|
1846
|
+
`${packageManager} install`,
|
|
1847
|
+
`${packageManager} run dev`
|
|
1848
|
+
].join("\n");
|
|
1849
|
+
p__namespace.note(nextSteps, "Next steps");
|
|
1850
|
+
await promptAndOpenEditor(projectPath);
|
|
1851
|
+
p__namespace.outro(color__default.green("Happy coding! \u2728"));
|
|
1852
|
+
} catch (error) {
|
|
1853
|
+
spinner.stop("Failed to create project");
|
|
1854
|
+
p__namespace.log.error(String(error));
|
|
1855
|
+
process.exit(1);
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
async function handleInteractiveMonorepoMode(monorepoRoot) {
|
|
1859
|
+
const choice = await p__namespace.select({
|
|
1860
|
+
message: "Detected monorepo workspace",
|
|
1861
|
+
options: [
|
|
1862
|
+
{ value: "add", label: "Add new package to this workspace" },
|
|
1863
|
+
{ value: "standalone", label: "Create standalone project" }
|
|
1864
|
+
],
|
|
1865
|
+
initialValue: "add"
|
|
1866
|
+
});
|
|
1867
|
+
if (p__namespace.isCancel(choice)) {
|
|
1868
|
+
p__namespace.cancel("Operation cancelled.");
|
|
1869
|
+
process.exit(0);
|
|
1870
|
+
}
|
|
1871
|
+
if (choice === "add") {
|
|
1872
|
+
const inheritedTooling = await detectWorkspaceTooling(monorepoRoot);
|
|
1873
|
+
if (inheritedTooling.linter || inheritedTooling.formatter) {
|
|
1874
|
+
const toolingInfo = [
|
|
1875
|
+
inheritedTooling.linter && `linter: ${inheritedTooling.linter}`,
|
|
1876
|
+
inheritedTooling.formatter && `formatter: ${inheritedTooling.formatter}`
|
|
1877
|
+
].filter(Boolean).join(", ");
|
|
1878
|
+
p__namespace.log.info(`Using workspace tooling (${toolingInfo})`);
|
|
1879
|
+
}
|
|
1880
|
+
const scope = await getMonorepoScope(monorepoRoot);
|
|
1881
|
+
let addMore = true;
|
|
1882
|
+
while (addMore) {
|
|
1883
|
+
addMore = await createPackageInWorkspace(
|
|
1884
|
+
monorepoRoot,
|
|
1885
|
+
"pnpm",
|
|
1886
|
+
inheritedTooling,
|
|
1887
|
+
scope
|
|
1888
|
+
);
|
|
1889
|
+
}
|
|
1890
|
+
p__namespace.note(
|
|
1891
|
+
[`cd ${monorepoRoot}`, "pnpm install", "pnpm run dev"].join("\n"),
|
|
1892
|
+
"Next steps"
|
|
1893
|
+
);
|
|
1894
|
+
await promptAndOpenEditor(monorepoRoot);
|
|
1895
|
+
p__namespace.outro(color__default.green("Happy coding! \u2728"));
|
|
1896
|
+
process.exit(0);
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
960
1899
|
async function main() {
|
|
961
|
-
const program = new commander.Command().name("create-krispya").description(
|
|
1900
|
+
const program = new commander.Command().name("create-krispya").description(
|
|
1901
|
+
"CLI for creating Vanilla, React, and React Three Fiber projects"
|
|
1902
|
+
).argument("[name]", "name for the project").option("--type <type>", "project type: app or library (default: app)").option(
|
|
962
1903
|
"--bundler <bundler>",
|
|
963
1904
|
"library bundler: unbuild or tsdown (default: unbuild, only for libraries)"
|
|
964
1905
|
).option(
|
|
965
1906
|
"--template <type>",
|
|
966
1907
|
"project template: vanilla, vanilla-js, react, react-js, r3f, r3f-js (default: vanilla)"
|
|
967
|
-
).option(
|
|
1908
|
+
).option(
|
|
1909
|
+
"--linter <type>",
|
|
1910
|
+
"linter: eslint, oxlint, or biome (default: oxlint)"
|
|
1911
|
+
).option(
|
|
1912
|
+
"--formatter <type>",
|
|
1913
|
+
"formatter: prettier, oxfmt, or biome (default: oxfmt)"
|
|
1914
|
+
).option("--drei", "add @react-three/drei (r3f only)").option("--handle", "add @react-three/handle (r3f only)").option("--leva", "add leva (r3f only)").option("--postprocessing", "add @react-three/postprocessing (r3f only)").option("--rapier", "add @react-three/rapier (r3f only)").option("--xr", "add @react-three/xr (r3f only)").option("--uikit", "add @react-three/uikit (r3f only)").option("--offscreen", "add @react-three/offscreen (r3f only)").option("--zustand", "add zustand (r3f only)").option("--koota", "add koota (r3f only)").option("--triplex", "set up triplex development environment (r3f only)").option("--viverse", "set up viverse deployment (r3f only)").option(
|
|
1915
|
+
"--package-manager <manager>",
|
|
1916
|
+
"specify package manager (e.g. npm, yarn, pnpm)"
|
|
1917
|
+
).option(
|
|
968
1918
|
"--pnpm-manage-versions",
|
|
969
1919
|
"enable manage-package-manager-versions in pnpm-workspace.yaml (default: true)"
|
|
970
1920
|
).option(
|
|
@@ -973,7 +1923,22 @@ async function main() {
|
|
|
973
1923
|
).option(
|
|
974
1924
|
"--node-version <version>",
|
|
975
1925
|
'set Node.js version for engines.node field (default: "latest")'
|
|
976
|
-
).option(
|
|
1926
|
+
).option(
|
|
1927
|
+
"--workspace",
|
|
1928
|
+
"Add package to current monorepo workspace (non-interactive)"
|
|
1929
|
+
).option(
|
|
1930
|
+
"--dir <directory>",
|
|
1931
|
+
"Target directory for --workspace (default: apps/ or packages/)"
|
|
1932
|
+
).option("--clear-config", "Clear saved preferences (e.g. editor choice)").option("--config-path", "Print the path to the config file").option(
|
|
1933
|
+
"--check",
|
|
1934
|
+
"Check if current directory is in a valid monorepo workspace"
|
|
1935
|
+
).option("--fix", "Fix monorepo by generating missing .config packages").option(
|
|
1936
|
+
"--path <directory>",
|
|
1937
|
+
"Run in specified directory instead of current working directory"
|
|
1938
|
+
).action(async (name, options) => {
|
|
1939
|
+
if (options.path) {
|
|
1940
|
+
process.chdir(options.path);
|
|
1941
|
+
}
|
|
977
1942
|
if (options.clearConfig) {
|
|
978
1943
|
clearConfig();
|
|
979
1944
|
console.log("Configuration cleared.");
|
|
@@ -983,44 +1948,57 @@ async function main() {
|
|
|
983
1948
|
console.log(getConfigPath());
|
|
984
1949
|
process.exit(0);
|
|
985
1950
|
}
|
|
1951
|
+
if (name?.startsWith("-")) {
|
|
1952
|
+
switch (name) {
|
|
1953
|
+
case "--version":
|
|
1954
|
+
case "-V":
|
|
1955
|
+
console.log(pkg.version);
|
|
1956
|
+
process.exit(0);
|
|
1957
|
+
case "--help":
|
|
1958
|
+
case "-h":
|
|
1959
|
+
program.help();
|
|
1960
|
+
break;
|
|
1961
|
+
case "--clear-config":
|
|
1962
|
+
clearConfig();
|
|
1963
|
+
console.log("Configuration cleared.");
|
|
1964
|
+
process.exit(0);
|
|
1965
|
+
case "--config-path":
|
|
1966
|
+
console.log(getConfigPath());
|
|
1967
|
+
process.exit(0);
|
|
1968
|
+
case "--check":
|
|
1969
|
+
await handleCheckCommand();
|
|
1970
|
+
break;
|
|
1971
|
+
case "--fix":
|
|
1972
|
+
options.fix = true;
|
|
1973
|
+
break;
|
|
1974
|
+
default:
|
|
1975
|
+
console.error(color__default.red(`Unknown option: ${name}`));
|
|
1976
|
+
process.exit(1);
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1979
|
+
if (options.check) {
|
|
1980
|
+
await handleCheckCommand();
|
|
1981
|
+
}
|
|
1982
|
+
if (options.fix) {
|
|
1983
|
+
await handleFixCommand(options);
|
|
1984
|
+
}
|
|
1985
|
+
if (options.dir && !options.workspace) {
|
|
1986
|
+
console.error(color__default.red("Error:") + " --dir requires --workspace flag");
|
|
1987
|
+
console.log(
|
|
1988
|
+
color__default.dim(
|
|
1989
|
+
" Example: pnpm create krispya my-lib --workspace --dir examples"
|
|
1990
|
+
)
|
|
1991
|
+
);
|
|
1992
|
+
process.exit(1);
|
|
1993
|
+
}
|
|
1994
|
+
if (options.workspace) {
|
|
1995
|
+
await handleWorkspaceCommand(name, options);
|
|
1996
|
+
}
|
|
986
1997
|
console.clear();
|
|
987
1998
|
p__namespace.intro(color__default.bgCyan(color__default.black(` create-krispya v${pkg.version} `)));
|
|
988
1999
|
const monorepoRoot = await detectMonorepoRoot();
|
|
989
2000
|
if (monorepoRoot && Object.keys(options).length === 0) {
|
|
990
|
-
|
|
991
|
-
message: "Detected monorepo workspace",
|
|
992
|
-
options: [
|
|
993
|
-
{ value: "add", label: "Add new package to this workspace" },
|
|
994
|
-
{ value: "standalone", label: "Create standalone project" }
|
|
995
|
-
],
|
|
996
|
-
initialValue: "add"
|
|
997
|
-
});
|
|
998
|
-
if (p__namespace.isCancel(choice)) {
|
|
999
|
-
p__namespace.cancel("Operation cancelled.");
|
|
1000
|
-
process.exit(0);
|
|
1001
|
-
}
|
|
1002
|
-
if (choice === "add") {
|
|
1003
|
-
const inheritedTooling = await detectWorkspaceTooling(monorepoRoot);
|
|
1004
|
-
if (inheritedTooling.linter || inheritedTooling.formatter) {
|
|
1005
|
-
const toolingInfo = [
|
|
1006
|
-
inheritedTooling.linter && `linter: ${inheritedTooling.linter}`,
|
|
1007
|
-
inheritedTooling.formatter && `formatter: ${inheritedTooling.formatter}`
|
|
1008
|
-
].filter(Boolean).join(", ");
|
|
1009
|
-
p__namespace.log.info(`Using workspace tooling (${toolingInfo})`);
|
|
1010
|
-
}
|
|
1011
|
-
const scope = await getMonorepoScope(monorepoRoot);
|
|
1012
|
-
let addMore = true;
|
|
1013
|
-
while (addMore) {
|
|
1014
|
-
addMore = await createPackageInWorkspace(monorepoRoot, "pnpm", inheritedTooling, scope);
|
|
1015
|
-
}
|
|
1016
|
-
p__namespace.note(
|
|
1017
|
-
[`cd ${monorepoRoot}`, "pnpm install", "pnpm run dev"].join("\n"),
|
|
1018
|
-
"Next steps"
|
|
1019
|
-
);
|
|
1020
|
-
await promptAndOpenEditor(monorepoRoot);
|
|
1021
|
-
p__namespace.outro(color__default.green("Happy coding! \u2728"));
|
|
1022
|
-
process.exit(0);
|
|
1023
|
-
}
|
|
2001
|
+
await handleInteractiveMonorepoMode(monorepoRoot);
|
|
1024
2002
|
}
|
|
1025
2003
|
let generateOptions;
|
|
1026
2004
|
if (Object.keys(options).length > 0) {
|
|
@@ -1057,226 +2035,9 @@ async function main() {
|
|
|
1057
2035
|
generateOptions = await promptForOptions(name);
|
|
1058
2036
|
}
|
|
1059
2037
|
if (generateOptions.projectType === "monorepo") {
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
generateOptions.pnpmVersion = await index.getLatestPnpmVersion();
|
|
1064
|
-
}
|
|
1065
|
-
const nodeVersion2 = generateOptions.nodeVersion ?? "latest";
|
|
1066
|
-
if (nodeVersion2 === "latest") {
|
|
1067
|
-
generateOptions.nodeVersion = await index.getLatestNodeVersion();
|
|
1068
|
-
}
|
|
1069
|
-
const basePath2 = path.join(process$1.cwd(), generateOptions.name);
|
|
1070
|
-
const s2 = p__namespace.spinner();
|
|
1071
|
-
s2.start("Creating monorepo workspace...");
|
|
1072
|
-
try {
|
|
1073
|
-
const { files } = generateMonorepo({
|
|
1074
|
-
name: generateOptions.name,
|
|
1075
|
-
linter: generateOptions.linter ?? "oxlint",
|
|
1076
|
-
formatter: generateOptions.formatter ?? "oxfmt",
|
|
1077
|
-
packageManager: packageManager2,
|
|
1078
|
-
pnpmVersion: generateOptions.pnpmVersion,
|
|
1079
|
-
pnpmManageVersions: generateOptions.pnpmManageVersions,
|
|
1080
|
-
nodeVersion: generateOptions.nodeVersion
|
|
1081
|
-
});
|
|
1082
|
-
const filePaths = Object.keys(files).sort();
|
|
1083
|
-
for (const filePath of filePaths) {
|
|
1084
|
-
const fullFilePath = path.join(basePath2, filePath);
|
|
1085
|
-
await promises.mkdir(path.dirname(fullFilePath), { recursive: true });
|
|
1086
|
-
const file = files[filePath];
|
|
1087
|
-
if (file.type === "text") {
|
|
1088
|
-
await promises.writeFile(fullFilePath, file.content);
|
|
1089
|
-
}
|
|
1090
|
-
}
|
|
1091
|
-
s2.stop(color__default.green.inverse(" \u2713 Monorepo workspace created! "));
|
|
1092
|
-
const newMonorepoTooling = {
|
|
1093
|
-
linter: generateOptions.linter,
|
|
1094
|
-
formatter: generateOptions.formatter
|
|
1095
|
-
};
|
|
1096
|
-
const scope = generateOptions.name;
|
|
1097
|
-
let addMore = true;
|
|
1098
|
-
while (addMore) {
|
|
1099
|
-
addMore = await createPackageInWorkspace(basePath2, packageManager2, newMonorepoTooling, scope);
|
|
1100
|
-
}
|
|
1101
|
-
const nextSteps = [
|
|
1102
|
-
`cd ${generateOptions.name}`,
|
|
1103
|
-
`${packageManager2} install`,
|
|
1104
|
-
`${packageManager2} run dev`
|
|
1105
|
-
].join("\n");
|
|
1106
|
-
p__namespace.note(nextSteps, "Next steps");
|
|
1107
|
-
await promptAndOpenEditor(basePath2);
|
|
1108
|
-
p__namespace.outro(color__default.green("Happy coding! \u2728"));
|
|
1109
|
-
process.exit(0);
|
|
1110
|
-
} catch (error) {
|
|
1111
|
-
s2.stop("Failed to create monorepo workspace");
|
|
1112
|
-
p__namespace.log.error(String(error));
|
|
1113
|
-
process.exit(1);
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
const base = generateOptions.template ? index.getBaseTemplate(generateOptions.template) : "vanilla";
|
|
1117
|
-
const defaultFallbackName = base === "vanilla" ? "vanilla-app" : base === "react" ? "react-app" : "react-three-app";
|
|
1118
|
-
generateOptions.name ??= defaultFallbackName;
|
|
1119
|
-
const packageManager = generateOptions.packageManager || "pnpm";
|
|
1120
|
-
if (packageManager === "pnpm") {
|
|
1121
|
-
generateOptions.pnpmVersion = await index.getLatestPnpmVersion();
|
|
1122
|
-
}
|
|
1123
|
-
const nodeVersion = generateOptions.nodeVersion ?? "latest";
|
|
1124
|
-
if (nodeVersion === "latest") {
|
|
1125
|
-
generateOptions.nodeVersion = await index.getLatestNodeVersion();
|
|
1126
|
-
}
|
|
1127
|
-
const versions = {};
|
|
1128
|
-
const versionPromises = [];
|
|
1129
|
-
const isLibrary = generateOptions.projectType === "library";
|
|
1130
|
-
const testing = generateOptions.testing ?? (isLibrary ? "vitest" : "none");
|
|
1131
|
-
if (testing === "vitest") {
|
|
1132
|
-
versionPromises.push(
|
|
1133
|
-
index.getLatestNpmVersion("vitest", "4.0.0").then((v) => {
|
|
1134
|
-
versions.vitest = v;
|
|
1135
|
-
})
|
|
1136
|
-
);
|
|
1137
|
-
}
|
|
1138
|
-
if (!isLibrary) {
|
|
1139
|
-
versionPromises.push(
|
|
1140
|
-
index.getLatestNpmVersion("vite", "6.3.4").then((v) => {
|
|
1141
|
-
versions.vite = v;
|
|
1142
|
-
})
|
|
1143
|
-
);
|
|
1144
|
-
}
|
|
1145
|
-
const linter = generateOptions.linter ?? "oxlint";
|
|
1146
|
-
if (linter === "eslint") {
|
|
1147
|
-
versionPromises.push(
|
|
1148
|
-
index.getLatestNpmVersion("eslint", "9.17.0").then((v) => {
|
|
1149
|
-
versions.eslint = v;
|
|
1150
|
-
})
|
|
1151
|
-
);
|
|
1152
|
-
} else if (linter === "oxlint") {
|
|
1153
|
-
versionPromises.push(
|
|
1154
|
-
index.getLatestNpmVersion("oxlint", "0.16.0").then((v) => {
|
|
1155
|
-
versions.oxlint = v;
|
|
1156
|
-
})
|
|
1157
|
-
);
|
|
1158
|
-
} else if (linter === "biome") {
|
|
1159
|
-
versionPromises.push(
|
|
1160
|
-
index.getLatestNpmVersion("@biomejs/biome", "1.9.4").then((v) => {
|
|
1161
|
-
versions.biome = v;
|
|
1162
|
-
})
|
|
1163
|
-
);
|
|
1164
|
-
}
|
|
1165
|
-
const formatter = generateOptions.formatter ?? "oxfmt";
|
|
1166
|
-
if (formatter === "prettier") {
|
|
1167
|
-
versionPromises.push(
|
|
1168
|
-
index.getLatestNpmVersion("prettier", "3.4.2").then((v) => {
|
|
1169
|
-
versions.prettier = v;
|
|
1170
|
-
})
|
|
1171
|
-
);
|
|
1172
|
-
} else if (formatter === "oxfmt") {
|
|
1173
|
-
versionPromises.push(
|
|
1174
|
-
index.getLatestNpmVersion("oxfmt", "0.1.0").then((v) => {
|
|
1175
|
-
versions.oxfmt = v;
|
|
1176
|
-
})
|
|
1177
|
-
);
|
|
1178
|
-
} else if (formatter === "biome" && linter !== "biome") {
|
|
1179
|
-
versionPromises.push(
|
|
1180
|
-
index.getLatestNpmVersion("@biomejs/biome", "1.9.4").then((v) => {
|
|
1181
|
-
versions.biome = v;
|
|
1182
|
-
})
|
|
1183
|
-
);
|
|
1184
|
-
}
|
|
1185
|
-
await Promise.all(versionPromises);
|
|
1186
|
-
generateOptions.versions = versions;
|
|
1187
|
-
const basePath = path.join(process$1.cwd(), generateOptions.name);
|
|
1188
|
-
const s = p__namespace.spinner();
|
|
1189
|
-
s.start("Creating project...");
|
|
1190
|
-
try {
|
|
1191
|
-
const files = index.generate(generateOptions);
|
|
1192
|
-
const filePaths = Object.keys(files).sort();
|
|
1193
|
-
for (const filePath of filePaths) {
|
|
1194
|
-
const fullFilePath = path.join(basePath, filePath);
|
|
1195
|
-
await promises.mkdir(path.dirname(fullFilePath), { recursive: true });
|
|
1196
|
-
const file = files[filePath];
|
|
1197
|
-
if (file.type === "text") {
|
|
1198
|
-
await promises.writeFile(fullFilePath, file.content);
|
|
1199
|
-
} else {
|
|
1200
|
-
const response = await undici.fetch(file.url);
|
|
1201
|
-
await promises.writeFile(fullFilePath, response.body);
|
|
1202
|
-
}
|
|
1203
|
-
}
|
|
1204
|
-
s.stop(color__default.green.inverse(" \u2713 Project created! "));
|
|
1205
|
-
const isLibrary2 = generateOptions.projectType === "library";
|
|
1206
|
-
const nextSteps = isLibrary2 ? [
|
|
1207
|
-
`cd ${generateOptions.name}`,
|
|
1208
|
-
`${packageManager} install`,
|
|
1209
|
-
`${packageManager} run build`
|
|
1210
|
-
].join("\n") : [
|
|
1211
|
-
`cd ${generateOptions.name}`,
|
|
1212
|
-
`${packageManager} install`,
|
|
1213
|
-
`${packageManager} run dev`
|
|
1214
|
-
].join("\n");
|
|
1215
|
-
p__namespace.note(nextSteps, "Next steps");
|
|
1216
|
-
const savedEditor = getPreferredEditor();
|
|
1217
|
-
let selectedEditor;
|
|
1218
|
-
if (savedEditor && savedEditor !== "skip") {
|
|
1219
|
-
const useDefault = await p__namespace.confirm({
|
|
1220
|
-
message: `Open in editor? ${color__default.dim(`(${editorNames[savedEditor]})`)}`,
|
|
1221
|
-
initialValue: true
|
|
1222
|
-
});
|
|
1223
|
-
if (p__namespace.isCancel(useDefault)) {
|
|
1224
|
-
selectedEditor = void 0;
|
|
1225
|
-
} else if (useDefault) {
|
|
1226
|
-
selectedEditor = savedEditor;
|
|
1227
|
-
} else {
|
|
1228
|
-
selectedEditor = "skip";
|
|
1229
|
-
}
|
|
1230
|
-
} else {
|
|
1231
|
-
const openEditor = await p__namespace.select({
|
|
1232
|
-
message: "Open project in editor?",
|
|
1233
|
-
options: [
|
|
1234
|
-
{ value: "skip", label: "Skip" },
|
|
1235
|
-
{ value: "cursor", label: "Cursor" },
|
|
1236
|
-
{ value: "code", label: "VS Code" },
|
|
1237
|
-
{ value: "webstorm", label: "WebStorm" }
|
|
1238
|
-
],
|
|
1239
|
-
initialValue: "skip"
|
|
1240
|
-
});
|
|
1241
|
-
if (!p__namespace.isCancel(openEditor)) {
|
|
1242
|
-
selectedEditor = openEditor;
|
|
1243
|
-
const saveChoice = await p__namespace.confirm({
|
|
1244
|
-
message: `Save ${editorNames[selectedEditor] ?? "Skip"} as default editor?`,
|
|
1245
|
-
initialValue: true
|
|
1246
|
-
});
|
|
1247
|
-
if (!p__namespace.isCancel(saveChoice) && saveChoice) {
|
|
1248
|
-
setPreferredEditor(selectedEditor);
|
|
1249
|
-
if (selectedEditor === "cursor" || selectedEditor === "code") {
|
|
1250
|
-
const reuseChoice = await p__namespace.confirm({
|
|
1251
|
-
message: "Reuse current window when opening projects?",
|
|
1252
|
-
initialValue: false
|
|
1253
|
-
});
|
|
1254
|
-
if (!p__namespace.isCancel(reuseChoice)) {
|
|
1255
|
-
setReuseWindow(reuseChoice);
|
|
1256
|
-
}
|
|
1257
|
-
}
|
|
1258
|
-
}
|
|
1259
|
-
}
|
|
1260
|
-
}
|
|
1261
|
-
if (selectedEditor && selectedEditor !== "skip") {
|
|
1262
|
-
try {
|
|
1263
|
-
await openInEditor(
|
|
1264
|
-
selectedEditor,
|
|
1265
|
-
basePath,
|
|
1266
|
-
getReuseWindow()
|
|
1267
|
-
);
|
|
1268
|
-
p__namespace.log.success(`Opening in ${editorNames[selectedEditor]}...`);
|
|
1269
|
-
} catch {
|
|
1270
|
-
p__namespace.log.warn(
|
|
1271
|
-
`Could not open ${editorNames[selectedEditor]}. Make sure the CLI command is in your PATH.`
|
|
1272
|
-
);
|
|
1273
|
-
}
|
|
1274
|
-
}
|
|
1275
|
-
p__namespace.outro(color__default.green("Happy coding! \u2728"));
|
|
1276
|
-
} catch (error) {
|
|
1277
|
-
s.stop("Failed to create project");
|
|
1278
|
-
p__namespace.log.error(String(error));
|
|
1279
|
-
process.exit(1);
|
|
2038
|
+
await handleMonorepoCreation(generateOptions);
|
|
2039
|
+
} else {
|
|
2040
|
+
await handleStandaloneProjectCreation(generateOptions);
|
|
1280
2041
|
}
|
|
1281
2042
|
});
|
|
1282
2043
|
await program.parseAsync();
|