create-krispya 0.5.1 → 0.5.3
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 +1090 -337
- package/dist/cli.mjs +1094 -341
- 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
|
}
|
|
@@ -732,56 +821,264 @@ async function getMonorepoScope(monorepoRoot) {
|
|
|
732
821
|
}
|
|
733
822
|
async function getWorkspacePackages(monorepoRoot) {
|
|
734
823
|
const packagesDir = path.join(monorepoRoot, "packages");
|
|
735
|
-
const packages = [];
|
|
736
824
|
try {
|
|
737
825
|
const { readdir } = await import('fs/promises');
|
|
738
826
|
const entries = await readdir(packagesDir, { withFileTypes: true });
|
|
827
|
+
const names = [];
|
|
739
828
|
for (const entry of entries) {
|
|
740
|
-
if (entry.isDirectory())
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
}
|
|
829
|
+
if (!entry.isDirectory()) continue;
|
|
830
|
+
try {
|
|
831
|
+
const content = await promises.readFile(
|
|
832
|
+
path.join(packagesDir, entry.name, "package.json"),
|
|
833
|
+
"utf-8"
|
|
834
|
+
);
|
|
835
|
+
const pkg2 = JSON.parse(content);
|
|
836
|
+
if (pkg2.name) names.push(pkg2.name);
|
|
837
|
+
} catch {
|
|
750
838
|
}
|
|
751
839
|
}
|
|
840
|
+
return names;
|
|
841
|
+
} catch {
|
|
842
|
+
return [];
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
async function ensureConfigInWorkspace(monorepoRoot) {
|
|
846
|
+
const workspacePath = path.join(monorepoRoot, "pnpm-workspace.yaml");
|
|
847
|
+
let content;
|
|
848
|
+
try {
|
|
849
|
+
content = await promises.readFile(workspacePath, "utf-8");
|
|
850
|
+
} catch {
|
|
851
|
+
content = `packages:
|
|
852
|
+
- ".config/*"
|
|
853
|
+
- "packages/*"
|
|
854
|
+
`;
|
|
855
|
+
await promises.writeFile(workspacePath, content);
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
858
|
+
if (content.includes(".config/*") || content.includes('".config/*"')) {
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
861
|
+
const lines = content.split("\n");
|
|
862
|
+
const packagesIndex = lines.findIndex(
|
|
863
|
+
(line) => line.trim().startsWith("packages:")
|
|
864
|
+
);
|
|
865
|
+
if (packagesIndex === -1) {
|
|
866
|
+
content = `packages:
|
|
867
|
+
- ".config/*"
|
|
868
|
+
${content}`;
|
|
869
|
+
} else {
|
|
870
|
+
lines.splice(packagesIndex + 1, 0, ' - ".config/*"');
|
|
871
|
+
content = lines.join("\n");
|
|
872
|
+
}
|
|
873
|
+
await promises.writeFile(workspacePath, content);
|
|
874
|
+
}
|
|
875
|
+
async function migrateEslintConfig(monorepoRoot, files) {
|
|
876
|
+
const configBasePath = ".config/eslint";
|
|
877
|
+
const existingConfigPath = path.join(monorepoRoot, "eslint.config.js");
|
|
878
|
+
let existingContent;
|
|
879
|
+
try {
|
|
880
|
+
existingContent = await promises.readFile(existingConfigPath, "utf-8");
|
|
881
|
+
} catch {
|
|
882
|
+
index.generateEslintConfigPackage(files);
|
|
883
|
+
return;
|
|
884
|
+
}
|
|
885
|
+
files[`${configBasePath}/package.json`] = {
|
|
886
|
+
type: "text",
|
|
887
|
+
content: JSON.stringify(
|
|
888
|
+
{
|
|
889
|
+
name: "@config/eslint",
|
|
890
|
+
version: "0.1.0",
|
|
891
|
+
private: true,
|
|
892
|
+
type: "module",
|
|
893
|
+
exports: {
|
|
894
|
+
"./base": "./base.js",
|
|
895
|
+
"./react": "./react.js"
|
|
896
|
+
}
|
|
897
|
+
},
|
|
898
|
+
null,
|
|
899
|
+
2
|
|
900
|
+
)
|
|
901
|
+
};
|
|
902
|
+
files[`${configBasePath}/README.md`] = {
|
|
903
|
+
type: "text",
|
|
904
|
+
content: `# \`@config/eslint\`
|
|
905
|
+
|
|
906
|
+
Shared ESLint configurations.
|
|
907
|
+
|
|
908
|
+
## Usage
|
|
909
|
+
|
|
910
|
+
In your package's \`eslint.config.js\`:
|
|
911
|
+
|
|
912
|
+
\`\`\`js
|
|
913
|
+
import base from "@config/eslint/base";
|
|
914
|
+
|
|
915
|
+
export default [...base];
|
|
916
|
+
\`\`\`
|
|
917
|
+
|
|
918
|
+
## Available Configs
|
|
919
|
+
|
|
920
|
+
- \`base\` - Base ESLint rules (migrated from root)
|
|
921
|
+
- \`react\` - React-specific rules
|
|
922
|
+
`
|
|
923
|
+
};
|
|
924
|
+
files[`${configBasePath}/base.js`] = {
|
|
925
|
+
type: "text",
|
|
926
|
+
content: existingContent
|
|
927
|
+
};
|
|
928
|
+
files[`${configBasePath}/react.js`] = {
|
|
929
|
+
type: "text",
|
|
930
|
+
content: `import react from "eslint-plugin-react";
|
|
931
|
+
import reactHooks from "eslint-plugin-react-hooks";
|
|
932
|
+
|
|
933
|
+
export default [
|
|
934
|
+
{
|
|
935
|
+
plugins: {
|
|
936
|
+
react,
|
|
937
|
+
"react-hooks": reactHooks,
|
|
938
|
+
},
|
|
939
|
+
rules: {
|
|
940
|
+
...react.configs.recommended.rules,
|
|
941
|
+
...reactHooks.configs.recommended.rules,
|
|
942
|
+
"react/react-in-jsx-scope": "off",
|
|
943
|
+
},
|
|
944
|
+
settings: {
|
|
945
|
+
react: {
|
|
946
|
+
version: "detect",
|
|
947
|
+
},
|
|
948
|
+
},
|
|
949
|
+
},
|
|
950
|
+
];
|
|
951
|
+
`
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
async function migratePrettierConfig(monorepoRoot, files) {
|
|
955
|
+
const configBasePath = ".config/prettier";
|
|
956
|
+
const existingConfigPath = path.join(monorepoRoot, ".prettierrc.json");
|
|
957
|
+
let existingContent;
|
|
958
|
+
try {
|
|
959
|
+
existingContent = await promises.readFile(existingConfigPath, "utf-8");
|
|
752
960
|
} catch {
|
|
961
|
+
index.generatePrettierConfigPackage(files);
|
|
962
|
+
return;
|
|
753
963
|
}
|
|
754
|
-
|
|
964
|
+
files[`${configBasePath}/package.json`] = {
|
|
965
|
+
type: "text",
|
|
966
|
+
content: JSON.stringify(
|
|
967
|
+
{
|
|
968
|
+
name: "@config/prettier",
|
|
969
|
+
version: "0.1.0",
|
|
970
|
+
private: true,
|
|
971
|
+
exports: {
|
|
972
|
+
"./base": "./base.json"
|
|
973
|
+
}
|
|
974
|
+
},
|
|
975
|
+
null,
|
|
976
|
+
2
|
|
977
|
+
)
|
|
978
|
+
};
|
|
979
|
+
files[`${configBasePath}/README.md`] = {
|
|
980
|
+
type: "text",
|
|
981
|
+
content: `# \`@config/prettier\`
|
|
982
|
+
|
|
983
|
+
Shared Prettier configurations.
|
|
984
|
+
|
|
985
|
+
## Usage
|
|
986
|
+
|
|
987
|
+
In your package's \`.prettierrc\`:
|
|
988
|
+
|
|
989
|
+
\`\`\`json
|
|
990
|
+
"@config/prettier/base"
|
|
991
|
+
\`\`\`
|
|
992
|
+
|
|
993
|
+
Or in \`package.json\`:
|
|
994
|
+
|
|
995
|
+
\`\`\`json
|
|
996
|
+
{
|
|
997
|
+
"prettier": "@config/prettier/base"
|
|
998
|
+
}
|
|
999
|
+
\`\`\`
|
|
1000
|
+
|
|
1001
|
+
## Available Configs
|
|
1002
|
+
|
|
1003
|
+
- \`base\` - Base Prettier rules (migrated from root)
|
|
1004
|
+
`
|
|
1005
|
+
};
|
|
1006
|
+
files[`${configBasePath}/base.json`] = {
|
|
1007
|
+
type: "text",
|
|
1008
|
+
content: existingContent
|
|
1009
|
+
};
|
|
755
1010
|
}
|
|
756
1011
|
async function createPackageInWorkspace(monorepoRoot, packageManager, inheritedTooling, scope) {
|
|
1012
|
+
const workspaceDirectories = await parseWorkspaceDirectories(monorepoRoot);
|
|
1013
|
+
const defaultDirectories = ["apps", "packages"];
|
|
1014
|
+
const hasCustomDirectories = workspaceDirectories.length > 0 && !workspaceDirectories.every((dir) => defaultDirectories.includes(dir));
|
|
757
1015
|
const packageType = await promptForInitialPackage();
|
|
758
1016
|
if (packageType === "skip") {
|
|
759
1017
|
return false;
|
|
760
1018
|
}
|
|
1019
|
+
const defaultDir = packageType === "app" ? "apps" : "packages";
|
|
761
1020
|
const packageNameInput = await p__namespace.text({
|
|
762
1021
|
message: "Package name?",
|
|
763
|
-
|
|
1022
|
+
initialValue: `@${scope}/`,
|
|
764
1023
|
validate: (value) => {
|
|
765
|
-
|
|
1024
|
+
const validationError = index.validatePackageName(value);
|
|
1025
|
+
if (validationError) return validationError;
|
|
1026
|
+
const dirName = value.includes("/") ? value.split("/").pop() : value;
|
|
1027
|
+
if (!dirName) return "Package name is required";
|
|
1028
|
+
if (!hasCustomDirectories) {
|
|
1029
|
+
const targetPath = path.join(monorepoRoot, defaultDir, dirName);
|
|
1030
|
+
try {
|
|
1031
|
+
const { statSync } = require$1("fs");
|
|
1032
|
+
statSync(targetPath);
|
|
1033
|
+
return `Directory ${defaultDir}/${dirName} already exists`;
|
|
1034
|
+
} catch {
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
766
1037
|
}
|
|
767
1038
|
});
|
|
768
1039
|
if (p__namespace.isCancel(packageNameInput)) {
|
|
769
1040
|
return false;
|
|
770
1041
|
}
|
|
771
|
-
const
|
|
772
|
-
const
|
|
773
|
-
const targetDir = packageType === "app" ? "apps" : "packages";
|
|
774
|
-
const packagePath = path.join(targetDir, shortName);
|
|
775
|
-
const workspaceRoot = "../..";
|
|
1042
|
+
const scopedName = packageNameInput;
|
|
1043
|
+
const shortName = scopedName.includes("/") ? scopedName.split("/").pop() : scopedName;
|
|
776
1044
|
const packageOptions = await promptForPackageOptions(
|
|
777
1045
|
scopedName,
|
|
778
1046
|
packageType,
|
|
779
1047
|
inheritedTooling
|
|
780
1048
|
);
|
|
1049
|
+
let targetDir = defaultDir;
|
|
1050
|
+
if (hasCustomDirectories && workspaceDirectories.length > 0) {
|
|
1051
|
+
const dirChoice = await p__namespace.select({
|
|
1052
|
+
message: "Target directory",
|
|
1053
|
+
options: workspaceDirectories.map((dir) => ({
|
|
1054
|
+
value: dir,
|
|
1055
|
+
label: dir
|
|
1056
|
+
})),
|
|
1057
|
+
initialValue: workspaceDirectories.includes(defaultDir) ? defaultDir : workspaceDirectories[0]
|
|
1058
|
+
});
|
|
1059
|
+
if (p__namespace.isCancel(dirChoice)) {
|
|
1060
|
+
return false;
|
|
1061
|
+
}
|
|
1062
|
+
targetDir = dirChoice;
|
|
1063
|
+
const targetPath = path.join(monorepoRoot, targetDir, shortName);
|
|
1064
|
+
try {
|
|
1065
|
+
const { statSync } = require$1("fs");
|
|
1066
|
+
statSync(targetPath);
|
|
1067
|
+
p__namespace.log.error(`Directory ${targetDir}/${shortName} already exists`);
|
|
1068
|
+
return false;
|
|
1069
|
+
} catch {
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
const relativePkgPath = path.join(targetDir, shortName);
|
|
1073
|
+
const workspaceRoot = calculateWorkspaceRoot(relativePkgPath);
|
|
781
1074
|
packageOptions.workspaceRoot = workspaceRoot;
|
|
782
1075
|
packageOptions.name = scopedName;
|
|
783
1076
|
if (packageManager === "pnpm") {
|
|
784
1077
|
packageOptions.pnpmVersion = await index.getLatestPnpmVersion();
|
|
1078
|
+
} else if (packageManager === "yarn") {
|
|
1079
|
+
packageOptions.yarnVersion = await index.getLatestYarnVersion();
|
|
1080
|
+
} else if (packageManager === "npm") {
|
|
1081
|
+
packageOptions.npmVersion = await index.getLatestNpmCliVersion();
|
|
785
1082
|
}
|
|
786
1083
|
const nodeVersion = packageOptions.nodeVersion ?? "latest";
|
|
787
1084
|
if (nodeVersion === "latest") {
|
|
@@ -847,40 +1144,26 @@ async function createPackageInWorkspace(monorepoRoot, packageManager, inheritedT
|
|
|
847
1144
|
}
|
|
848
1145
|
await Promise.all(versionPromises);
|
|
849
1146
|
packageOptions.versions = versions;
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
required: false
|
|
860
|
-
});
|
|
861
|
-
if (!p__namespace.isCancel(selectedDeps) && selectedDeps.length > 0) {
|
|
862
|
-
packageOptions.workspaceDependencies = selectedDeps;
|
|
863
|
-
}
|
|
1147
|
+
const workspacePackages = packageType === "app" ? await getWorkspacePackages(monorepoRoot) : [];
|
|
1148
|
+
if (workspacePackages.length > 0) {
|
|
1149
|
+
const selectedDeps = await p__namespace.multiselect({
|
|
1150
|
+
message: "Add workspace dependencies?",
|
|
1151
|
+
options: workspacePackages.map((name) => ({ value: name, label: name })),
|
|
1152
|
+
required: false
|
|
1153
|
+
});
|
|
1154
|
+
if (!p__namespace.isCancel(selectedDeps) && selectedDeps.length > 0) {
|
|
1155
|
+
packageOptions.workspaceDependencies = selectedDeps;
|
|
864
1156
|
}
|
|
865
1157
|
}
|
|
866
|
-
const
|
|
867
|
-
const
|
|
868
|
-
|
|
1158
|
+
const outputPath = path.join(monorepoRoot, relativePkgPath);
|
|
1159
|
+
const spinner = p__namespace.spinner();
|
|
1160
|
+
spinner.start("Creating package...");
|
|
869
1161
|
try {
|
|
870
1162
|
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}! `));
|
|
1163
|
+
await writeGeneratedFiles(outputPath, files);
|
|
1164
|
+
spinner.stop(
|
|
1165
|
+
color__default.green.inverse(` \u2713 Package created at ${relativePkgPath}! `)
|
|
1166
|
+
);
|
|
884
1167
|
const addAnother = await p__namespace.select({
|
|
885
1168
|
message: "Add another package?",
|
|
886
1169
|
options: [
|
|
@@ -891,12 +1174,12 @@ async function createPackageInWorkspace(monorepoRoot, packageManager, inheritedT
|
|
|
891
1174
|
});
|
|
892
1175
|
return !p__namespace.isCancel(addAnother) && addAnother === "yes";
|
|
893
1176
|
} catch (error) {
|
|
894
|
-
|
|
1177
|
+
spinner.stop("Failed to create package");
|
|
895
1178
|
p__namespace.log.error(String(error));
|
|
896
1179
|
return false;
|
|
897
1180
|
}
|
|
898
1181
|
}
|
|
899
|
-
async function promptAndOpenEditor(
|
|
1182
|
+
async function promptAndOpenEditor(projectPath) {
|
|
900
1183
|
const savedEditor = getPreferredEditor();
|
|
901
1184
|
let selectedEditor;
|
|
902
1185
|
if (savedEditor && savedEditor !== "skip") {
|
|
@@ -946,7 +1229,7 @@ async function promptAndOpenEditor(basePath) {
|
|
|
946
1229
|
try {
|
|
947
1230
|
await openInEditor(
|
|
948
1231
|
selectedEditor,
|
|
949
|
-
|
|
1232
|
+
projectPath,
|
|
950
1233
|
getReuseWindow()
|
|
951
1234
|
);
|
|
952
1235
|
p__namespace.log.success(`Opening in ${editorNames[selectedEditor]}...`);
|
|
@@ -957,14 +1240,673 @@ async function promptAndOpenEditor(basePath) {
|
|
|
957
1240
|
}
|
|
958
1241
|
}
|
|
959
1242
|
}
|
|
1243
|
+
async function handleCheckCommand() {
|
|
1244
|
+
const monorepoRoot = await detectMonorepoRoot();
|
|
1245
|
+
if (!monorepoRoot) {
|
|
1246
|
+
console.log(color__default.red("\u2717") + " Not a monorepo workspace");
|
|
1247
|
+
process.exit(1);
|
|
1248
|
+
}
|
|
1249
|
+
const { valid, errors } = await validateWorkspace(monorepoRoot);
|
|
1250
|
+
if (valid) {
|
|
1251
|
+
console.log(color__default.green("\u2713") + " Valid monorepo workspace");
|
|
1252
|
+
console.log(color__default.dim(` ${monorepoRoot}`));
|
|
1253
|
+
} else {
|
|
1254
|
+
console.log(color__default.red("\u2717") + " Invalid monorepo workspace");
|
|
1255
|
+
console.log(color__default.dim(` ${monorepoRoot}`));
|
|
1256
|
+
for (const error of errors) {
|
|
1257
|
+
console.log(color__default.red(` \u2022 ${error}`));
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
process.exit(valid ? 0 : 1);
|
|
1261
|
+
}
|
|
1262
|
+
async function handleFixCommand(options) {
|
|
1263
|
+
const monorepoRoot = await detectMonorepoRoot();
|
|
1264
|
+
if (!monorepoRoot) {
|
|
1265
|
+
console.log(color__default.red("\u2717") + " Not a monorepo workspace");
|
|
1266
|
+
console.log(color__default.dim(" Run this command from within a monorepo"));
|
|
1267
|
+
process.exit(1);
|
|
1268
|
+
}
|
|
1269
|
+
const { valid, errors } = await validateWorkspace(monorepoRoot);
|
|
1270
|
+
if (valid) {
|
|
1271
|
+
console.log(color__default.green("\u2713") + " Workspace is already valid");
|
|
1272
|
+
console.log(color__default.dim(` ${monorepoRoot}`));
|
|
1273
|
+
process.exit(0);
|
|
1274
|
+
}
|
|
1275
|
+
console.log(color__default.yellow("!") + " Invalid monorepo workspace");
|
|
1276
|
+
for (const error of errors) {
|
|
1277
|
+
console.log(color__default.dim(` \u2022 ${error}`));
|
|
1278
|
+
}
|
|
1279
|
+
console.log();
|
|
1280
|
+
const tooling = await detectWorkspaceTooling(monorepoRoot);
|
|
1281
|
+
const existingConfigs = await detectExistingConfigs(monorepoRoot);
|
|
1282
|
+
const detectedLinter = tooling.linter ?? existingConfigs.linter ?? "oxlint";
|
|
1283
|
+
const detectedFormatter = tooling.formatter ?? existingConfigs.formatter ?? "oxfmt";
|
|
1284
|
+
const isNonInteractive = options.linter && options.formatter;
|
|
1285
|
+
let linter;
|
|
1286
|
+
let formatter;
|
|
1287
|
+
if (isNonInteractive) {
|
|
1288
|
+
linter = options.linter;
|
|
1289
|
+
formatter = options.formatter;
|
|
1290
|
+
} else {
|
|
1291
|
+
const linterChoice = await p__namespace.select({
|
|
1292
|
+
message: "Linter",
|
|
1293
|
+
options: [
|
|
1294
|
+
{
|
|
1295
|
+
value: "oxlint",
|
|
1296
|
+
label: "oxlint" + (tooling.linter === "oxlint" ? color__default.dim(" (installed)") : "")
|
|
1297
|
+
},
|
|
1298
|
+
{
|
|
1299
|
+
value: "eslint",
|
|
1300
|
+
label: "eslint" + (tooling.linter === "eslint" || existingConfigs.linter === "eslint" ? color__default.dim(" (installed)") : "")
|
|
1301
|
+
},
|
|
1302
|
+
{
|
|
1303
|
+
value: "biome",
|
|
1304
|
+
label: "biome" + (tooling.linter === "biome" ? color__default.dim(" (installed)") : "")
|
|
1305
|
+
}
|
|
1306
|
+
],
|
|
1307
|
+
initialValue: detectedLinter
|
|
1308
|
+
});
|
|
1309
|
+
if (p__namespace.isCancel(linterChoice)) {
|
|
1310
|
+
p__namespace.cancel("Operation cancelled.");
|
|
1311
|
+
process.exit(0);
|
|
1312
|
+
}
|
|
1313
|
+
const formatterChoice = await p__namespace.select({
|
|
1314
|
+
message: "Formatter",
|
|
1315
|
+
options: [
|
|
1316
|
+
{
|
|
1317
|
+
value: "oxfmt",
|
|
1318
|
+
label: "oxfmt" + (tooling.formatter === "oxfmt" ? color__default.dim(" (installed)") : "")
|
|
1319
|
+
},
|
|
1320
|
+
{
|
|
1321
|
+
value: "prettier",
|
|
1322
|
+
label: "prettier" + (tooling.formatter === "prettier" || existingConfigs.formatter === "prettier" ? color__default.dim(" (installed)") : "")
|
|
1323
|
+
},
|
|
1324
|
+
{
|
|
1325
|
+
value: "biome",
|
|
1326
|
+
label: "biome" + (tooling.formatter === "biome" ? color__default.dim(" (installed)") : "")
|
|
1327
|
+
}
|
|
1328
|
+
],
|
|
1329
|
+
initialValue: detectedFormatter
|
|
1330
|
+
});
|
|
1331
|
+
if (p__namespace.isCancel(formatterChoice)) {
|
|
1332
|
+
p__namespace.cancel("Operation cancelled.");
|
|
1333
|
+
process.exit(0);
|
|
1334
|
+
}
|
|
1335
|
+
linter = linterChoice;
|
|
1336
|
+
formatter = formatterChoice;
|
|
1337
|
+
}
|
|
1338
|
+
console.log();
|
|
1339
|
+
const spinner = p__namespace.spinner();
|
|
1340
|
+
spinner.start("Fixing workspace...");
|
|
1341
|
+
try {
|
|
1342
|
+
const files = {};
|
|
1343
|
+
const tsConfigExists = await fileExists(
|
|
1344
|
+
path.join(monorepoRoot, ".config/typescript/package.json")
|
|
1345
|
+
);
|
|
1346
|
+
if (!tsConfigExists) {
|
|
1347
|
+
index.generateTypescriptConfigPackage(files);
|
|
1348
|
+
}
|
|
1349
|
+
if (linter === "oxlint") {
|
|
1350
|
+
const oxlintExists = await fileExists(
|
|
1351
|
+
path.join(monorepoRoot, ".config/oxlint/package.json")
|
|
1352
|
+
);
|
|
1353
|
+
if (!oxlintExists) index.generateOxlintConfigPackage(files);
|
|
1354
|
+
} else if (linter === "eslint") {
|
|
1355
|
+
const eslintPkgExists = await fileExists(
|
|
1356
|
+
path.join(monorepoRoot, ".config/eslint/package.json")
|
|
1357
|
+
);
|
|
1358
|
+
if (!eslintPkgExists) {
|
|
1359
|
+
if (existingConfigs.eslintConfigPath) {
|
|
1360
|
+
await migrateEslintConfig(monorepoRoot, files);
|
|
1361
|
+
} else {
|
|
1362
|
+
index.generateEslintConfigPackage(files);
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
if (formatter === "oxfmt") {
|
|
1367
|
+
const oxfmtExists = await fileExists(
|
|
1368
|
+
path.join(monorepoRoot, ".config/oxfmt/package.json")
|
|
1369
|
+
);
|
|
1370
|
+
if (!oxfmtExists) index.generateOxfmtConfigPackage(files);
|
|
1371
|
+
} else if (formatter === "prettier") {
|
|
1372
|
+
const prettierPkgExists = await fileExists(
|
|
1373
|
+
path.join(monorepoRoot, ".config/prettier/package.json")
|
|
1374
|
+
);
|
|
1375
|
+
if (!prettierPkgExists) {
|
|
1376
|
+
if (existingConfigs.prettierConfigPath) {
|
|
1377
|
+
await migratePrettierConfig(monorepoRoot, files);
|
|
1378
|
+
} else {
|
|
1379
|
+
index.generatePrettierConfigPackage(files);
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
if ((linter === "biome" || formatter === "biome") && !existingConfigs.biomeConfigPath) {
|
|
1384
|
+
const biomeConfig = {
|
|
1385
|
+
$schema: "https://biomejs.dev/schemas/1.9.4/schema.json",
|
|
1386
|
+
vcs: {
|
|
1387
|
+
enabled: true,
|
|
1388
|
+
clientKind: "git",
|
|
1389
|
+
useIgnoreFile: true
|
|
1390
|
+
},
|
|
1391
|
+
linter: {
|
|
1392
|
+
enabled: linter === "biome",
|
|
1393
|
+
rules: {
|
|
1394
|
+
recommended: true
|
|
1395
|
+
}
|
|
1396
|
+
},
|
|
1397
|
+
formatter: {
|
|
1398
|
+
enabled: formatter === "biome"
|
|
1399
|
+
}
|
|
1400
|
+
};
|
|
1401
|
+
files["biome.json"] = {
|
|
1402
|
+
type: "text",
|
|
1403
|
+
content: JSON.stringify(biomeConfig, null, 2)
|
|
1404
|
+
};
|
|
1405
|
+
}
|
|
1406
|
+
for (const [filePath, file] of Object.entries(files)) {
|
|
1407
|
+
const fullPath = path.join(monorepoRoot, filePath);
|
|
1408
|
+
await promises.mkdir(path.dirname(fullPath), { recursive: true });
|
|
1409
|
+
await promises.writeFile(fullPath, file.content);
|
|
1410
|
+
}
|
|
1411
|
+
await ensureConfigInWorkspace(monorepoRoot);
|
|
1412
|
+
if (existingConfigs.eslintConfigPath && linter === "eslint") {
|
|
1413
|
+
try {
|
|
1414
|
+
await promises.unlink(existingConfigs.eslintConfigPath);
|
|
1415
|
+
} catch {
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
if (existingConfigs.prettierConfigPath && formatter === "prettier") {
|
|
1419
|
+
try {
|
|
1420
|
+
await promises.unlink(existingConfigs.prettierConfigPath);
|
|
1421
|
+
} catch {
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
spinner.stop(color__default.green("\u2713") + " Workspace fixed!");
|
|
1425
|
+
const generated = Object.keys(files).filter(
|
|
1426
|
+
(f) => f.endsWith("package.json")
|
|
1427
|
+
);
|
|
1428
|
+
for (const pkgFile of generated) {
|
|
1429
|
+
const pkgName = pkgFile.replace("/package.json", "");
|
|
1430
|
+
console.log(color__default.dim(` Generated ${pkgName}`));
|
|
1431
|
+
}
|
|
1432
|
+
const vscodeSettingsExists = await fileExists(
|
|
1433
|
+
path.join(monorepoRoot, ".vscode/settings.json")
|
|
1434
|
+
);
|
|
1435
|
+
const vscodeExtensionsExists = await fileExists(
|
|
1436
|
+
path.join(monorepoRoot, ".vscode/extensions.json")
|
|
1437
|
+
);
|
|
1438
|
+
const vscodeExists = vscodeSettingsExists && vscodeExtensionsExists;
|
|
1439
|
+
if (!vscodeExists) {
|
|
1440
|
+
let addVscode = false;
|
|
1441
|
+
if (isNonInteractive) {
|
|
1442
|
+
addVscode = true;
|
|
1443
|
+
} else {
|
|
1444
|
+
const vscodeChoice = await p__namespace.confirm({
|
|
1445
|
+
message: "Generate VS Code settings?",
|
|
1446
|
+
initialValue: true
|
|
1447
|
+
});
|
|
1448
|
+
addVscode = !p__namespace.isCancel(vscodeChoice) && vscodeChoice;
|
|
1449
|
+
}
|
|
1450
|
+
if (addVscode) {
|
|
1451
|
+
const vscodeFiles = {};
|
|
1452
|
+
index.generateVscodeFiles(vscodeFiles, linter, formatter);
|
|
1453
|
+
for (const [filePath, file] of Object.entries(vscodeFiles)) {
|
|
1454
|
+
const fullPath = path.join(monorepoRoot, filePath);
|
|
1455
|
+
await promises.mkdir(path.dirname(fullPath), { recursive: true });
|
|
1456
|
+
await promises.writeFile(fullPath, file.content);
|
|
1457
|
+
}
|
|
1458
|
+
console.log(color__default.dim(" Generated .vscode/settings.json"));
|
|
1459
|
+
console.log(color__default.dim(" Generated .vscode/extensions.json"));
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
const aiFilePaths = {
|
|
1463
|
+
"cursor-rules": ".cursor/rules",
|
|
1464
|
+
"agents-md": "AGENTS.md",
|
|
1465
|
+
"claude-md": "CLAUDE.md",
|
|
1466
|
+
"copilot-md": ".github/copilot-instructions.md"
|
|
1467
|
+
};
|
|
1468
|
+
const existingAiFiles = [];
|
|
1469
|
+
for (const [choice, path$1] of Object.entries(aiFilePaths)) {
|
|
1470
|
+
if (await fileExists(path.join(monorepoRoot, path$1))) {
|
|
1471
|
+
existingAiFiles.push(choice);
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
let selectedAiFiles = [];
|
|
1475
|
+
const savedAiFiles = getAiFiles();
|
|
1476
|
+
const availableChoices = ["cursor-rules", "agents-md", "claude-md", "copilot-md"].filter((c) => !existingAiFiles.includes(c));
|
|
1477
|
+
if (availableChoices.length === 0) {
|
|
1478
|
+
} else if (isNonInteractive) {
|
|
1479
|
+
const preferred = savedAiFiles ?? ["cursor-rules"];
|
|
1480
|
+
selectedAiFiles = preferred.filter((f) => availableChoices.includes(f));
|
|
1481
|
+
} else if (savedAiFiles && savedAiFiles.length > 0) {
|
|
1482
|
+
const availableSaved = savedAiFiles.filter(
|
|
1483
|
+
(f) => availableChoices.includes(f)
|
|
1484
|
+
);
|
|
1485
|
+
if (availableSaved.length > 0) {
|
|
1486
|
+
const savedLabels = availableSaved.map((f) => aiFilePaths[f]).join(", ");
|
|
1487
|
+
const useDefault = await p__namespace.confirm({
|
|
1488
|
+
message: `Generate AI instruction files? ${color__default.dim(
|
|
1489
|
+
`(${savedLabels})`
|
|
1490
|
+
)}`,
|
|
1491
|
+
initialValue: true
|
|
1492
|
+
});
|
|
1493
|
+
if (!p__namespace.isCancel(useDefault) && useDefault) {
|
|
1494
|
+
selectedAiFiles = availableSaved;
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
} else {
|
|
1498
|
+
const aiFilesChoice = await p__namespace.multiselect({
|
|
1499
|
+
message: "Generate AI instruction files?",
|
|
1500
|
+
options: availableChoices.map((c) => ({
|
|
1501
|
+
value: c,
|
|
1502
|
+
label: aiFilePaths[c],
|
|
1503
|
+
hint: c === "cursor-rules" ? "Cursor AI" : c === "agents-md" ? "GitHub Copilot, general" : c === "claude-md" ? "Claude" : "GitHub Copilot"
|
|
1504
|
+
})),
|
|
1505
|
+
required: false
|
|
1506
|
+
});
|
|
1507
|
+
if (!p__namespace.isCancel(aiFilesChoice) && aiFilesChoice.length > 0) {
|
|
1508
|
+
selectedAiFiles = aiFilesChoice;
|
|
1509
|
+
const saveChoice = await p__namespace.confirm({
|
|
1510
|
+
message: "Save as default for future?",
|
|
1511
|
+
initialValue: true
|
|
1512
|
+
});
|
|
1513
|
+
if (!p__namespace.isCancel(saveChoice) && saveChoice) {
|
|
1514
|
+
setAiFiles(selectedAiFiles);
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
if (selectedAiFiles.length > 0) {
|
|
1519
|
+
const scope = await getMonorepoScope(monorepoRoot);
|
|
1520
|
+
const aiFilesOutput = {};
|
|
1521
|
+
index.generateAiFiles(aiFilesOutput, {
|
|
1522
|
+
name: scope,
|
|
1523
|
+
packageManager: "pnpm",
|
|
1524
|
+
linter,
|
|
1525
|
+
formatter,
|
|
1526
|
+
aiFiles: selectedAiFiles
|
|
1527
|
+
});
|
|
1528
|
+
for (const [filePath, file] of Object.entries(aiFilesOutput)) {
|
|
1529
|
+
const fullPath = path.join(monorepoRoot, filePath);
|
|
1530
|
+
await promises.mkdir(path.dirname(fullPath), { recursive: true });
|
|
1531
|
+
await promises.writeFile(fullPath, file.content);
|
|
1532
|
+
console.log(color__default.dim(` Generated ${filePath}`));
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
process.exit(0);
|
|
1536
|
+
} catch (error) {
|
|
1537
|
+
spinner.stop(color__default.red("\u2717") + " Failed to fix workspace");
|
|
1538
|
+
console.error(error);
|
|
1539
|
+
process.exit(1);
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
async function handleWorkspaceCommand(name, options) {
|
|
1543
|
+
const monorepoRoot = await detectMonorepoRoot();
|
|
1544
|
+
if (!monorepoRoot) {
|
|
1545
|
+
console.error(
|
|
1546
|
+
color__default.red("Error:") + " --workspace flag requires being inside a monorepo"
|
|
1547
|
+
);
|
|
1548
|
+
process.exit(1);
|
|
1549
|
+
}
|
|
1550
|
+
if (!name) {
|
|
1551
|
+
console.error(
|
|
1552
|
+
color__default.red("Error:") + " Package name is required with --workspace flag"
|
|
1553
|
+
);
|
|
1554
|
+
console.log(
|
|
1555
|
+
color__default.dim(
|
|
1556
|
+
" Example: pnpm create krispya my-lib --workspace --type library"
|
|
1557
|
+
)
|
|
1558
|
+
);
|
|
1559
|
+
process.exit(1);
|
|
1560
|
+
}
|
|
1561
|
+
const scope = await getMonorepoScope(monorepoRoot);
|
|
1562
|
+
const inheritedTooling = await detectWorkspaceTooling(monorepoRoot);
|
|
1563
|
+
const projectType = options.type ?? "app";
|
|
1564
|
+
const defaultDir = projectType === "library" ? "packages" : "apps";
|
|
1565
|
+
const targetDir = options.dir ?? defaultDir;
|
|
1566
|
+
const template = options.template ?? "vanilla";
|
|
1567
|
+
const baseTemplate = index.getBaseTemplate(template);
|
|
1568
|
+
const scopedName = name.startsWith("@") ? name : `@${scope}/${name}`;
|
|
1569
|
+
const fullPackagePath = path.join(monorepoRoot, targetDir, name);
|
|
1570
|
+
try {
|
|
1571
|
+
await promises.access(fullPackagePath, fs.constants.F_OK);
|
|
1572
|
+
console.error(
|
|
1573
|
+
color__default.red("Error:") + ` Directory ${targetDir}/${name} already exists`
|
|
1574
|
+
);
|
|
1575
|
+
process.exit(1);
|
|
1576
|
+
} catch {
|
|
1577
|
+
}
|
|
1578
|
+
const versions = {};
|
|
1579
|
+
const versionPromises = [];
|
|
1580
|
+
const isLibrary = projectType === "library";
|
|
1581
|
+
if (!isLibrary) {
|
|
1582
|
+
versionPromises.push(
|
|
1583
|
+
index.getLatestNpmVersion("vite", "6.3.4").then((v) => {
|
|
1584
|
+
versions.vite = v;
|
|
1585
|
+
})
|
|
1586
|
+
);
|
|
1587
|
+
}
|
|
1588
|
+
const linter = inheritedTooling.linter ?? options.linter ?? "oxlint";
|
|
1589
|
+
const formatter = inheritedTooling.formatter ?? options.formatter ?? "oxfmt";
|
|
1590
|
+
await Promise.all(versionPromises);
|
|
1591
|
+
const relativePkgPath = path.join(targetDir, name);
|
|
1592
|
+
const workspaceRoot = calculateWorkspaceRoot(relativePkgPath);
|
|
1593
|
+
const generateOptions = {
|
|
1594
|
+
name: scopedName,
|
|
1595
|
+
projectType,
|
|
1596
|
+
libraryBundler: isLibrary ? options.bundler ?? "unbuild" : void 0,
|
|
1597
|
+
template,
|
|
1598
|
+
linter,
|
|
1599
|
+
formatter,
|
|
1600
|
+
workspaceRoot,
|
|
1601
|
+
versions,
|
|
1602
|
+
...baseTemplate === "r3f" && {
|
|
1603
|
+
drei: options.drei ? {} : void 0,
|
|
1604
|
+
handle: options.handle ? {} : void 0,
|
|
1605
|
+
leva: options.leva ? {} : void 0,
|
|
1606
|
+
postprocessing: options.postprocessing ? {} : void 0,
|
|
1607
|
+
rapier: options.rapier ? {} : void 0,
|
|
1608
|
+
xr: options.xr ? {} : void 0,
|
|
1609
|
+
uikit: options.uikit ? {} : void 0,
|
|
1610
|
+
offscreen: options.offscreen ? {} : void 0,
|
|
1611
|
+
zustand: options.zustand ? {} : void 0,
|
|
1612
|
+
koota: options.koota ? {} : void 0,
|
|
1613
|
+
viverse: options.viverse ? {} : void 0,
|
|
1614
|
+
triplex: options.triplex ? {} : void 0
|
|
1615
|
+
}
|
|
1616
|
+
};
|
|
1617
|
+
console.log(
|
|
1618
|
+
color__default.cyan("Creating") + ` ${scopedName} in ${targetDir}/${name}...`
|
|
1619
|
+
);
|
|
1620
|
+
try {
|
|
1621
|
+
const files = index.generate(generateOptions);
|
|
1622
|
+
await writeGeneratedFiles(fullPackagePath, files);
|
|
1623
|
+
console.log(
|
|
1624
|
+
color__default.green("\u2713") + ` Created ${scopedName} at ${targetDir}/${name}`
|
|
1625
|
+
);
|
|
1626
|
+
process.exit(0);
|
|
1627
|
+
} catch (error) {
|
|
1628
|
+
console.error(color__default.red("Error:") + " Failed to create package");
|
|
1629
|
+
console.error(String(error));
|
|
1630
|
+
process.exit(1);
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
async function handleMonorepoCreation(generateOptions) {
|
|
1634
|
+
const { generateMonorepo } = await import('./chunks/index.cjs').then(function (n) { return n.monorepo; });
|
|
1635
|
+
const packageManager = generateOptions.packageManager || "pnpm";
|
|
1636
|
+
if (packageManager === "pnpm") {
|
|
1637
|
+
generateOptions.pnpmVersion = await index.getLatestPnpmVersion();
|
|
1638
|
+
} else if (packageManager === "yarn") {
|
|
1639
|
+
generateOptions.yarnVersion = await index.getLatestYarnVersion();
|
|
1640
|
+
} else if (packageManager === "npm") {
|
|
1641
|
+
generateOptions.npmVersion = await index.getLatestNpmCliVersion();
|
|
1642
|
+
}
|
|
1643
|
+
const nodeVersion = generateOptions.nodeVersion ?? "latest";
|
|
1644
|
+
if (nodeVersion === "latest") {
|
|
1645
|
+
generateOptions.nodeVersion = await index.getLatestNodeVersion();
|
|
1646
|
+
}
|
|
1647
|
+
const savedAiFiles = getAiFiles();
|
|
1648
|
+
let selectedAiFiles = [];
|
|
1649
|
+
if (savedAiFiles && savedAiFiles.length > 0) {
|
|
1650
|
+
const aiFileLabels = {
|
|
1651
|
+
"cursor-rules": ".cursor/rules",
|
|
1652
|
+
"agents-md": "AGENTS.md",
|
|
1653
|
+
"claude-md": "CLAUDE.md",
|
|
1654
|
+
"copilot-md": ".github/copilot-instructions.md"
|
|
1655
|
+
};
|
|
1656
|
+
const savedLabels = savedAiFiles.map((f) => aiFileLabels[f]).join(", ");
|
|
1657
|
+
const useDefault = await p__namespace.confirm({
|
|
1658
|
+
message: `Generate AI instruction files? ${color__default.dim(
|
|
1659
|
+
`(${savedLabels})`
|
|
1660
|
+
)}`,
|
|
1661
|
+
initialValue: true
|
|
1662
|
+
});
|
|
1663
|
+
if (!p__namespace.isCancel(useDefault) && useDefault) {
|
|
1664
|
+
selectedAiFiles = savedAiFiles;
|
|
1665
|
+
}
|
|
1666
|
+
} else {
|
|
1667
|
+
const aiFilesChoice = await p__namespace.multiselect({
|
|
1668
|
+
message: "Generate AI instruction files?",
|
|
1669
|
+
options: [
|
|
1670
|
+
{ value: "cursor-rules", label: ".cursor/rules", hint: "Cursor AI" },
|
|
1671
|
+
{
|
|
1672
|
+
value: "agents-md",
|
|
1673
|
+
label: "AGENTS.md",
|
|
1674
|
+
hint: "GitHub Copilot, general"
|
|
1675
|
+
},
|
|
1676
|
+
{ value: "claude-md", label: "CLAUDE.md", hint: "Claude" },
|
|
1677
|
+
{
|
|
1678
|
+
value: "copilot-md",
|
|
1679
|
+
label: ".github/copilot-instructions.md",
|
|
1680
|
+
hint: "GitHub Copilot"
|
|
1681
|
+
}
|
|
1682
|
+
],
|
|
1683
|
+
required: false
|
|
1684
|
+
});
|
|
1685
|
+
if (!p__namespace.isCancel(aiFilesChoice) && aiFilesChoice.length > 0) {
|
|
1686
|
+
selectedAiFiles = aiFilesChoice;
|
|
1687
|
+
const saveChoice = await p__namespace.confirm({
|
|
1688
|
+
message: "Save as default for future monorepos?",
|
|
1689
|
+
initialValue: true
|
|
1690
|
+
});
|
|
1691
|
+
if (!p__namespace.isCancel(saveChoice) && saveChoice) {
|
|
1692
|
+
setAiFiles(selectedAiFiles);
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
const projectPath = path.join(process$1.cwd(), generateOptions.name);
|
|
1697
|
+
const spinner = p__namespace.spinner();
|
|
1698
|
+
spinner.start("Creating monorepo workspace...");
|
|
1699
|
+
try {
|
|
1700
|
+
const { files } = generateMonorepo({
|
|
1701
|
+
name: generateOptions.name,
|
|
1702
|
+
linter: generateOptions.linter ?? "oxlint",
|
|
1703
|
+
formatter: generateOptions.formatter ?? "oxfmt",
|
|
1704
|
+
packageManager,
|
|
1705
|
+
pnpmVersion: generateOptions.pnpmVersion,
|
|
1706
|
+
pnpmManageVersions: generateOptions.pnpmManageVersions,
|
|
1707
|
+
nodeVersion: generateOptions.nodeVersion,
|
|
1708
|
+
aiFiles: selectedAiFiles.length > 0 ? selectedAiFiles : void 0
|
|
1709
|
+
});
|
|
1710
|
+
const filePaths = Object.keys(files).sort();
|
|
1711
|
+
for (const filePath of filePaths) {
|
|
1712
|
+
const fullFilePath = path.join(projectPath, filePath);
|
|
1713
|
+
await promises.mkdir(path.dirname(fullFilePath), { recursive: true });
|
|
1714
|
+
const file = files[filePath];
|
|
1715
|
+
if (file.type === "text") {
|
|
1716
|
+
await promises.writeFile(fullFilePath, file.content);
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
spinner.stop(color__default.green.inverse(" \u2713 Monorepo workspace created! "));
|
|
1720
|
+
const newMonorepoTooling = {
|
|
1721
|
+
linter: generateOptions.linter,
|
|
1722
|
+
formatter: generateOptions.formatter
|
|
1723
|
+
};
|
|
1724
|
+
const scope = generateOptions.name;
|
|
1725
|
+
let addMore = true;
|
|
1726
|
+
while (addMore) {
|
|
1727
|
+
addMore = await createPackageInWorkspace(
|
|
1728
|
+
projectPath,
|
|
1729
|
+
packageManager,
|
|
1730
|
+
newMonorepoTooling,
|
|
1731
|
+
scope
|
|
1732
|
+
);
|
|
1733
|
+
}
|
|
1734
|
+
const nextSteps = [
|
|
1735
|
+
`cd ${generateOptions.name}`,
|
|
1736
|
+
`${packageManager} install`,
|
|
1737
|
+
`${packageManager} run dev`
|
|
1738
|
+
].join("\n");
|
|
1739
|
+
p__namespace.note(nextSteps, "Next steps");
|
|
1740
|
+
await promptAndOpenEditor(projectPath);
|
|
1741
|
+
p__namespace.outro(color__default.green("Happy coding! \u2728"));
|
|
1742
|
+
process.exit(0);
|
|
1743
|
+
} catch (error) {
|
|
1744
|
+
spinner.stop("Failed to create monorepo workspace");
|
|
1745
|
+
p__namespace.log.error(String(error));
|
|
1746
|
+
process.exit(1);
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
async function handleStandaloneProjectCreation(generateOptions) {
|
|
1750
|
+
const base = generateOptions.template ? index.getBaseTemplate(generateOptions.template) : "vanilla";
|
|
1751
|
+
const defaultFallbackName = base === "vanilla" ? "vanilla-app" : base === "react" ? "react-app" : "react-three-app";
|
|
1752
|
+
generateOptions.name ??= defaultFallbackName;
|
|
1753
|
+
const packageManager = generateOptions.packageManager || "pnpm";
|
|
1754
|
+
if (packageManager === "pnpm") {
|
|
1755
|
+
generateOptions.pnpmVersion = await index.getLatestPnpmVersion();
|
|
1756
|
+
} else if (packageManager === "yarn") {
|
|
1757
|
+
generateOptions.yarnVersion = await index.getLatestYarnVersion();
|
|
1758
|
+
} else if (packageManager === "npm") {
|
|
1759
|
+
generateOptions.npmVersion = await index.getLatestNpmCliVersion();
|
|
1760
|
+
}
|
|
1761
|
+
const nodeVersion = generateOptions.nodeVersion ?? "latest";
|
|
1762
|
+
if (nodeVersion === "latest") {
|
|
1763
|
+
generateOptions.nodeVersion = await index.getLatestNodeVersion();
|
|
1764
|
+
}
|
|
1765
|
+
const versions = {};
|
|
1766
|
+
const versionPromises = [];
|
|
1767
|
+
const isLibrary = generateOptions.projectType === "library";
|
|
1768
|
+
const testing = generateOptions.testing ?? (isLibrary ? "vitest" : "none");
|
|
1769
|
+
if (testing === "vitest") {
|
|
1770
|
+
versionPromises.push(
|
|
1771
|
+
index.getLatestNpmVersion("vitest", "4.0.0").then((v) => {
|
|
1772
|
+
versions.vitest = v;
|
|
1773
|
+
})
|
|
1774
|
+
);
|
|
1775
|
+
}
|
|
1776
|
+
if (!isLibrary) {
|
|
1777
|
+
versionPromises.push(
|
|
1778
|
+
index.getLatestNpmVersion("vite", "6.3.4").then((v) => {
|
|
1779
|
+
versions.vite = v;
|
|
1780
|
+
})
|
|
1781
|
+
);
|
|
1782
|
+
}
|
|
1783
|
+
const linter = generateOptions.linter ?? "oxlint";
|
|
1784
|
+
if (linter === "eslint") {
|
|
1785
|
+
versionPromises.push(
|
|
1786
|
+
index.getLatestNpmVersion("eslint", "9.17.0").then((v) => {
|
|
1787
|
+
versions.eslint = v;
|
|
1788
|
+
})
|
|
1789
|
+
);
|
|
1790
|
+
} else if (linter === "oxlint") {
|
|
1791
|
+
versionPromises.push(
|
|
1792
|
+
index.getLatestNpmVersion("oxlint", "0.16.0").then((v) => {
|
|
1793
|
+
versions.oxlint = v;
|
|
1794
|
+
})
|
|
1795
|
+
);
|
|
1796
|
+
} else if (linter === "biome") {
|
|
1797
|
+
versionPromises.push(
|
|
1798
|
+
index.getLatestNpmVersion("@biomejs/biome", "1.9.4").then((v) => {
|
|
1799
|
+
versions.biome = v;
|
|
1800
|
+
})
|
|
1801
|
+
);
|
|
1802
|
+
}
|
|
1803
|
+
const formatter = generateOptions.formatter ?? "oxfmt";
|
|
1804
|
+
if (formatter === "prettier") {
|
|
1805
|
+
versionPromises.push(
|
|
1806
|
+
index.getLatestNpmVersion("prettier", "3.4.2").then((v) => {
|
|
1807
|
+
versions.prettier = v;
|
|
1808
|
+
})
|
|
1809
|
+
);
|
|
1810
|
+
} else if (formatter === "oxfmt") {
|
|
1811
|
+
versionPromises.push(
|
|
1812
|
+
index.getLatestNpmVersion("oxfmt", "0.1.0").then((v) => {
|
|
1813
|
+
versions.oxfmt = v;
|
|
1814
|
+
})
|
|
1815
|
+
);
|
|
1816
|
+
} else if (formatter === "biome" && linter !== "biome") {
|
|
1817
|
+
versionPromises.push(
|
|
1818
|
+
index.getLatestNpmVersion("@biomejs/biome", "1.9.4").then((v) => {
|
|
1819
|
+
versions.biome = v;
|
|
1820
|
+
})
|
|
1821
|
+
);
|
|
1822
|
+
}
|
|
1823
|
+
await Promise.all(versionPromises);
|
|
1824
|
+
generateOptions.versions = versions;
|
|
1825
|
+
const projectPath = path.join(process$1.cwd(), generateOptions.name);
|
|
1826
|
+
const spinner = p__namespace.spinner();
|
|
1827
|
+
spinner.start("Creating project...");
|
|
1828
|
+
try {
|
|
1829
|
+
const files = index.generate(generateOptions);
|
|
1830
|
+
await writeGeneratedFiles(projectPath, files);
|
|
1831
|
+
spinner.stop(color__default.green.inverse(" \u2713 Project created! "));
|
|
1832
|
+
const nextSteps = isLibrary ? [
|
|
1833
|
+
`cd ${generateOptions.name}`,
|
|
1834
|
+
`${packageManager} install`,
|
|
1835
|
+
`${packageManager} run build`
|
|
1836
|
+
].join("\n") : [
|
|
1837
|
+
`cd ${generateOptions.name}`,
|
|
1838
|
+
`${packageManager} install`,
|
|
1839
|
+
`${packageManager} run dev`
|
|
1840
|
+
].join("\n");
|
|
1841
|
+
p__namespace.note(nextSteps, "Next steps");
|
|
1842
|
+
await promptAndOpenEditor(projectPath);
|
|
1843
|
+
p__namespace.outro(color__default.green("Happy coding! \u2728"));
|
|
1844
|
+
} catch (error) {
|
|
1845
|
+
spinner.stop("Failed to create project");
|
|
1846
|
+
p__namespace.log.error(String(error));
|
|
1847
|
+
process.exit(1);
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
async function handleInteractiveMonorepoMode(monorepoRoot) {
|
|
1851
|
+
const choice = await p__namespace.select({
|
|
1852
|
+
message: "Detected monorepo workspace",
|
|
1853
|
+
options: [
|
|
1854
|
+
{ value: "add", label: "Add new package to this workspace" },
|
|
1855
|
+
{ value: "standalone", label: "Create standalone project" }
|
|
1856
|
+
],
|
|
1857
|
+
initialValue: "add"
|
|
1858
|
+
});
|
|
1859
|
+
if (p__namespace.isCancel(choice)) {
|
|
1860
|
+
p__namespace.cancel("Operation cancelled.");
|
|
1861
|
+
process.exit(0);
|
|
1862
|
+
}
|
|
1863
|
+
if (choice === "add") {
|
|
1864
|
+
const inheritedTooling = await detectWorkspaceTooling(monorepoRoot);
|
|
1865
|
+
if (inheritedTooling.linter || inheritedTooling.formatter) {
|
|
1866
|
+
const toolingInfo = [
|
|
1867
|
+
inheritedTooling.linter && `linter: ${inheritedTooling.linter}`,
|
|
1868
|
+
inheritedTooling.formatter && `formatter: ${inheritedTooling.formatter}`
|
|
1869
|
+
].filter(Boolean).join(", ");
|
|
1870
|
+
p__namespace.log.info(`Using workspace tooling (${toolingInfo})`);
|
|
1871
|
+
}
|
|
1872
|
+
const scope = await getMonorepoScope(monorepoRoot);
|
|
1873
|
+
let addMore = true;
|
|
1874
|
+
while (addMore) {
|
|
1875
|
+
addMore = await createPackageInWorkspace(
|
|
1876
|
+
monorepoRoot,
|
|
1877
|
+
"pnpm",
|
|
1878
|
+
inheritedTooling,
|
|
1879
|
+
scope
|
|
1880
|
+
);
|
|
1881
|
+
}
|
|
1882
|
+
p__namespace.note(
|
|
1883
|
+
[`cd ${monorepoRoot}`, "pnpm install", "pnpm run dev"].join("\n"),
|
|
1884
|
+
"Next steps"
|
|
1885
|
+
);
|
|
1886
|
+
await promptAndOpenEditor(monorepoRoot);
|
|
1887
|
+
p__namespace.outro(color__default.green("Happy coding! \u2728"));
|
|
1888
|
+
process.exit(0);
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
960
1891
|
async function main() {
|
|
961
|
-
const program = new commander.Command().name("create-krispya").description(
|
|
1892
|
+
const program = new commander.Command().name("create-krispya").description(
|
|
1893
|
+
"CLI for creating Vanilla, React, and React Three Fiber projects"
|
|
1894
|
+
).argument("[name]", "name for the project").option("--type <type>", "project type: app or library (default: app)").option(
|
|
962
1895
|
"--bundler <bundler>",
|
|
963
1896
|
"library bundler: unbuild or tsdown (default: unbuild, only for libraries)"
|
|
964
1897
|
).option(
|
|
965
1898
|
"--template <type>",
|
|
966
1899
|
"project template: vanilla, vanilla-js, react, react-js, r3f, r3f-js (default: vanilla)"
|
|
967
|
-
).option(
|
|
1900
|
+
).option(
|
|
1901
|
+
"--linter <type>",
|
|
1902
|
+
"linter: eslint, oxlint, or biome (default: oxlint)"
|
|
1903
|
+
).option(
|
|
1904
|
+
"--formatter <type>",
|
|
1905
|
+
"formatter: prettier, oxfmt, or biome (default: oxfmt)"
|
|
1906
|
+
).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(
|
|
1907
|
+
"--package-manager <manager>",
|
|
1908
|
+
"specify package manager (e.g. npm, yarn, pnpm)"
|
|
1909
|
+
).option(
|
|
968
1910
|
"--pnpm-manage-versions",
|
|
969
1911
|
"enable manage-package-manager-versions in pnpm-workspace.yaml (default: true)"
|
|
970
1912
|
).option(
|
|
@@ -973,7 +1915,22 @@ async function main() {
|
|
|
973
1915
|
).option(
|
|
974
1916
|
"--node-version <version>",
|
|
975
1917
|
'set Node.js version for engines.node field (default: "latest")'
|
|
976
|
-
).option(
|
|
1918
|
+
).option(
|
|
1919
|
+
"--workspace",
|
|
1920
|
+
"Add package to current monorepo workspace (non-interactive)"
|
|
1921
|
+
).option(
|
|
1922
|
+
"--dir <directory>",
|
|
1923
|
+
"Target directory for --workspace (default: apps/ or packages/)"
|
|
1924
|
+
).option("--clear-config", "Clear saved preferences (e.g. editor choice)").option("--config-path", "Print the path to the config file").option(
|
|
1925
|
+
"--check",
|
|
1926
|
+
"Check if current directory is in a valid monorepo workspace"
|
|
1927
|
+
).option("--fix", "Fix monorepo by generating missing .config packages").option(
|
|
1928
|
+
"--path <directory>",
|
|
1929
|
+
"Run in specified directory instead of current working directory"
|
|
1930
|
+
).action(async (name, options) => {
|
|
1931
|
+
if (options.path) {
|
|
1932
|
+
process.chdir(options.path);
|
|
1933
|
+
}
|
|
977
1934
|
if (options.clearConfig) {
|
|
978
1935
|
clearConfig();
|
|
979
1936
|
console.log("Configuration cleared.");
|
|
@@ -983,44 +1940,57 @@ async function main() {
|
|
|
983
1940
|
console.log(getConfigPath());
|
|
984
1941
|
process.exit(0);
|
|
985
1942
|
}
|
|
1943
|
+
if (name?.startsWith("-")) {
|
|
1944
|
+
switch (name) {
|
|
1945
|
+
case "--version":
|
|
1946
|
+
case "-V":
|
|
1947
|
+
console.log(pkg.version);
|
|
1948
|
+
process.exit(0);
|
|
1949
|
+
case "--help":
|
|
1950
|
+
case "-h":
|
|
1951
|
+
program.help();
|
|
1952
|
+
break;
|
|
1953
|
+
case "--clear-config":
|
|
1954
|
+
clearConfig();
|
|
1955
|
+
console.log("Configuration cleared.");
|
|
1956
|
+
process.exit(0);
|
|
1957
|
+
case "--config-path":
|
|
1958
|
+
console.log(getConfigPath());
|
|
1959
|
+
process.exit(0);
|
|
1960
|
+
case "--check":
|
|
1961
|
+
await handleCheckCommand();
|
|
1962
|
+
break;
|
|
1963
|
+
case "--fix":
|
|
1964
|
+
options.fix = true;
|
|
1965
|
+
break;
|
|
1966
|
+
default:
|
|
1967
|
+
console.error(color__default.red(`Unknown option: ${name}`));
|
|
1968
|
+
process.exit(1);
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
if (options.check) {
|
|
1972
|
+
await handleCheckCommand();
|
|
1973
|
+
}
|
|
1974
|
+
if (options.fix) {
|
|
1975
|
+
await handleFixCommand(options);
|
|
1976
|
+
}
|
|
1977
|
+
if (options.dir && !options.workspace) {
|
|
1978
|
+
console.error(color__default.red("Error:") + " --dir requires --workspace flag");
|
|
1979
|
+
console.log(
|
|
1980
|
+
color__default.dim(
|
|
1981
|
+
" Example: pnpm create krispya my-lib --workspace --dir examples"
|
|
1982
|
+
)
|
|
1983
|
+
);
|
|
1984
|
+
process.exit(1);
|
|
1985
|
+
}
|
|
1986
|
+
if (options.workspace) {
|
|
1987
|
+
await handleWorkspaceCommand(name, options);
|
|
1988
|
+
}
|
|
986
1989
|
console.clear();
|
|
987
1990
|
p__namespace.intro(color__default.bgCyan(color__default.black(` create-krispya v${pkg.version} `)));
|
|
988
1991
|
const monorepoRoot = await detectMonorepoRoot();
|
|
989
1992
|
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
|
-
}
|
|
1993
|
+
await handleInteractiveMonorepoMode(monorepoRoot);
|
|
1024
1994
|
}
|
|
1025
1995
|
let generateOptions;
|
|
1026
1996
|
if (Object.keys(options).length > 0) {
|
|
@@ -1057,226 +2027,9 @@ async function main() {
|
|
|
1057
2027
|
generateOptions = await promptForOptions(name);
|
|
1058
2028
|
}
|
|
1059
2029
|
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);
|
|
2030
|
+
await handleMonorepoCreation(generateOptions);
|
|
2031
|
+
} else {
|
|
2032
|
+
await handleStandaloneProjectCreation(generateOptions);
|
|
1280
2033
|
}
|
|
1281
2034
|
});
|
|
1282
2035
|
await program.parseAsync();
|