@lucaismyname/create-l1-stack 0.1.3 → 0.1.5
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/package.json +1 -1
- package/src/index.js +177 -0
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -13,6 +13,16 @@ import * as tar from 'tar'
|
|
|
13
13
|
const TEMPLATE_PKG_TS = '@lucaismyname/l1-template-ts'
|
|
14
14
|
const TEMPLATE_PATH_PREFIX = 'package/template/'
|
|
15
15
|
|
|
16
|
+
const CODE_QUALITY_PRETTIER_VERSION = '^3.6.2'
|
|
17
|
+
const CODE_QUALITY_HUSKY_VERSION = '^9.1.7'
|
|
18
|
+
const CODE_QUALITY_LINT_STAGED_VERSION = '^16.0.0'
|
|
19
|
+
|
|
20
|
+
const TESTING_VITEST_VERSION = '^3.2.4'
|
|
21
|
+
const TESTING_JS_DOM_VERSION = '^26.1.0'
|
|
22
|
+
const TESTING_TESTING_LIBRARY_REACT_VERSION = '^16.3.0'
|
|
23
|
+
const TESTING_TESTING_LIBRARY_JEST_DOM_VERSION = '^6.9.0'
|
|
24
|
+
const TESTING_TESTING_LIBRARY_USER_EVENT_VERSION = '^14.6.1'
|
|
25
|
+
|
|
16
26
|
const THEME_PRESETS = {
|
|
17
27
|
slate: {
|
|
18
28
|
light: {
|
|
@@ -740,6 +750,132 @@ async function applyDeployTarget(targetDir, targetKey) {
|
|
|
740
750
|
}
|
|
741
751
|
}
|
|
742
752
|
|
|
753
|
+
async function applyCodeQualityTools(targetDir) {
|
|
754
|
+
const pkgJsonPath = path.join(targetDir, 'package.json')
|
|
755
|
+
const pkg = await fse.readJson(pkgJsonPath)
|
|
756
|
+
|
|
757
|
+
pkg.devDependencies ??= {}
|
|
758
|
+
pkg.scripts ??= {}
|
|
759
|
+
|
|
760
|
+
pkg.devDependencies.prettier = CODE_QUALITY_PRETTIER_VERSION
|
|
761
|
+
pkg.devDependencies.husky = CODE_QUALITY_HUSKY_VERSION
|
|
762
|
+
pkg.devDependencies['lint-staged'] = CODE_QUALITY_LINT_STAGED_VERSION
|
|
763
|
+
|
|
764
|
+
pkg.scripts.format ??= 'prettier . --write'
|
|
765
|
+
pkg.scripts['format:check'] ??= 'prettier . --check'
|
|
766
|
+
pkg.scripts.prepare ??= 'husky'
|
|
767
|
+
|
|
768
|
+
pkg['lint-staged'] ??= {
|
|
769
|
+
'*.{ts,tsx,js,jsx,css,md,json}': ['prettier --write'],
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
await fse.writeJson(pkgJsonPath, pkg, { spaces: 2 })
|
|
773
|
+
|
|
774
|
+
const prettierRcPath = path.join(targetDir, '.prettierrc')
|
|
775
|
+
const prettierIgnorePath = path.join(targetDir, '.prettierignore')
|
|
776
|
+
|
|
777
|
+
const prettierRc = `{
|
|
778
|
+
"semi": false,
|
|
779
|
+
"singleQuote": true,
|
|
780
|
+
"trailingComma": "all"
|
|
781
|
+
}
|
|
782
|
+
`
|
|
783
|
+
|
|
784
|
+
const prettierIgnore = `node_modules
|
|
785
|
+
dist
|
|
786
|
+
coverage
|
|
787
|
+
.DS_Store
|
|
788
|
+
package-lock.json
|
|
789
|
+
pnpm-lock.yaml
|
|
790
|
+
yarn.lock
|
|
791
|
+
`
|
|
792
|
+
|
|
793
|
+
await fs.writeFile(prettierRcPath, prettierRc, 'utf8')
|
|
794
|
+
await fs.writeFile(prettierIgnorePath, prettierIgnore, 'utf8')
|
|
795
|
+
|
|
796
|
+
const huskyDir = path.join(targetDir, '.husky')
|
|
797
|
+
await fs.mkdir(huskyDir, { recursive: true })
|
|
798
|
+
|
|
799
|
+
const preCommitPath = path.join(huskyDir, 'pre-commit')
|
|
800
|
+
const preCommit = `#!/usr/bin/env sh
|
|
801
|
+
. "$(dirname -- \"$0\")/_/husky.sh"
|
|
802
|
+
|
|
803
|
+
npx lint-staged
|
|
804
|
+
`
|
|
805
|
+
await fs.writeFile(preCommitPath, preCommit, 'utf8')
|
|
806
|
+
try {
|
|
807
|
+
await fs.chmod(preCommitPath, 0o755)
|
|
808
|
+
} catch {
|
|
809
|
+
// ignore
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
async function applyTesting(targetDir) {
|
|
814
|
+
const pkgJsonPath = path.join(targetDir, 'package.json')
|
|
815
|
+
const pkg = await fse.readJson(pkgJsonPath)
|
|
816
|
+
|
|
817
|
+
pkg.devDependencies ??= {}
|
|
818
|
+
pkg.scripts ??= {}
|
|
819
|
+
|
|
820
|
+
pkg.devDependencies.vitest = TESTING_VITEST_VERSION
|
|
821
|
+
pkg.devDependencies.jsdom = TESTING_JS_DOM_VERSION
|
|
822
|
+
pkg.devDependencies['@testing-library/react'] = TESTING_TESTING_LIBRARY_REACT_VERSION
|
|
823
|
+
pkg.devDependencies['@testing-library/jest-dom'] = TESTING_TESTING_LIBRARY_JEST_DOM_VERSION
|
|
824
|
+
pkg.devDependencies['@testing-library/user-event'] = TESTING_TESTING_LIBRARY_USER_EVENT_VERSION
|
|
825
|
+
|
|
826
|
+
pkg.scripts.test ??= 'vitest'
|
|
827
|
+
pkg.scripts['test:run'] ??= 'vitest run'
|
|
828
|
+
|
|
829
|
+
await fse.writeJson(pkgJsonPath, pkg, { spaces: 2 })
|
|
830
|
+
|
|
831
|
+
const setupPath = path.join(targetDir, 'src', 'test', 'setup.ts')
|
|
832
|
+
await fs.mkdir(path.dirname(setupPath), { recursive: true })
|
|
833
|
+
await fs.writeFile(setupPath, `import '@testing-library/jest-dom/vitest'\n`, 'utf8')
|
|
834
|
+
|
|
835
|
+
const appTestPath = path.join(targetDir, 'src', 'App.test.tsx')
|
|
836
|
+
const appTest = `import { render, screen } from '@testing-library/react'\nimport { MemoryRouter } from 'react-router-dom'\n\nimport App from './App'\n\ntest('renders the app', () => {\n render(\n <MemoryRouter initialEntries={[\"/\"]}>\n <App />\n </MemoryRouter>\n )\n\n expect(screen.getByText(/hello world/i)).toBeInTheDocument()\n})\n`
|
|
837
|
+
await fs.writeFile(appTestPath, appTest, 'utf8')
|
|
838
|
+
|
|
839
|
+
const viteConfigPath = path.join(targetDir, 'vite.config.ts')
|
|
840
|
+
try {
|
|
841
|
+
const src = await fs.readFile(viteConfigPath, 'utf8')
|
|
842
|
+
let next = src
|
|
843
|
+
|
|
844
|
+
next = next.replace(
|
|
845
|
+
/import \{ defineConfig \} from 'vite'/g,
|
|
846
|
+
"import { defineConfig } from 'vitest/config'"
|
|
847
|
+
)
|
|
848
|
+
|
|
849
|
+
if (!/\btest\s*:\s*\{/m.test(next)) {
|
|
850
|
+
const testBlock = ` test: {\n environment: 'jsdom',\n globals: true,\n setupFiles: './src/test/setup.ts',\n },\n`
|
|
851
|
+
next = next.replace(/\n\}\)\s*\n?$/m, (m) => {
|
|
852
|
+
const before = next.slice(0, next.length - m.length)
|
|
853
|
+
const trimmedBefore = before.replace(/\s+$/g, '')
|
|
854
|
+
const needsComma = !trimmedBefore.endsWith(',') && !trimmedBefore.endsWith('{')
|
|
855
|
+
return `\n${needsComma ? ',' : ''}\n${testBlock}})\n`
|
|
856
|
+
})
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
if (next !== src) await fs.writeFile(viteConfigPath, next, 'utf8')
|
|
860
|
+
} catch {
|
|
861
|
+
// ignore
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
const tsconfigAppPath = path.join(targetDir, 'tsconfig.app.json')
|
|
865
|
+
try {
|
|
866
|
+
const tsconfig = await fse.readJson(tsconfigAppPath)
|
|
867
|
+
tsconfig.compilerOptions ??= {}
|
|
868
|
+
const existingTypes = Array.isArray(tsconfig.compilerOptions.types)
|
|
869
|
+
? tsconfig.compilerOptions.types
|
|
870
|
+
: ['vite/client']
|
|
871
|
+
if (!existingTypes.includes('vitest/globals')) existingTypes.push('vitest/globals')
|
|
872
|
+
tsconfig.compilerOptions.types = existingTypes
|
|
873
|
+
await fse.writeJson(tsconfigAppPath, tsconfig, { spaces: 2 })
|
|
874
|
+
} catch {
|
|
875
|
+
// ignore
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
|
|
743
879
|
function toKebabCaseName(input) {
|
|
744
880
|
return input
|
|
745
881
|
.trim()
|
|
@@ -1000,6 +1136,32 @@ async function main() {
|
|
|
1000
1136
|
process.exit(0)
|
|
1001
1137
|
}
|
|
1002
1138
|
|
|
1139
|
+
const codeQuality = await select({
|
|
1140
|
+
message: 'Add code quality tools (Prettier + Husky + lint-staged)?',
|
|
1141
|
+
options: [
|
|
1142
|
+
{ value: 'yes', label: 'Yes' },
|
|
1143
|
+
{ value: 'no', label: 'No' },
|
|
1144
|
+
],
|
|
1145
|
+
initialValue: 'yes',
|
|
1146
|
+
})
|
|
1147
|
+
if (isCancel(codeQuality)) {
|
|
1148
|
+
cancel('Cancelled')
|
|
1149
|
+
process.exit(0)
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
const testing = await select({
|
|
1153
|
+
message: 'Add testing (Vitest + Testing Library)?',
|
|
1154
|
+
options: [
|
|
1155
|
+
{ value: 'yes', label: 'Yes' },
|
|
1156
|
+
{ value: 'no', label: 'No' },
|
|
1157
|
+
],
|
|
1158
|
+
initialValue: 'yes',
|
|
1159
|
+
})
|
|
1160
|
+
if (isCancel(testing)) {
|
|
1161
|
+
cancel('Cancelled')
|
|
1162
|
+
process.exit(0)
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1003
1165
|
const targetDir = path.resolve(process.cwd(), projectName)
|
|
1004
1166
|
// NOTE: npm downloads the CLI package before prompts run.
|
|
1005
1167
|
// To avoid downloading both templates, we fetch only the chosen template from npm.
|
|
@@ -1063,6 +1225,13 @@ async function main() {
|
|
|
1063
1225
|
await applyContainerPreset(targetDir, language, containerPreset)
|
|
1064
1226
|
await applyDeployTarget(targetDir, deployTarget)
|
|
1065
1227
|
|
|
1228
|
+
if (codeQuality === 'yes') {
|
|
1229
|
+
await applyCodeQualityTools(targetDir)
|
|
1230
|
+
}
|
|
1231
|
+
if (testing === 'yes') {
|
|
1232
|
+
await applyTesting(targetDir)
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1066
1235
|
// Update package name
|
|
1067
1236
|
const pkgJsonPath = path.join(targetDir, 'package.json')
|
|
1068
1237
|
const pkg = await fse.readJson(pkgJsonPath)
|
|
@@ -1087,6 +1256,14 @@ async function main() {
|
|
|
1087
1256
|
if (shouldInstall === 'yes') {
|
|
1088
1257
|
try {
|
|
1089
1258
|
await execa(pm.name, pm.install, { cwd: targetDir, stdio: 'inherit' })
|
|
1259
|
+
|
|
1260
|
+
if (codeQuality === 'yes') {
|
|
1261
|
+
try {
|
|
1262
|
+
await execa('npx', ['husky', 'install'], { cwd: targetDir, stdio: 'inherit' })
|
|
1263
|
+
} catch {
|
|
1264
|
+
// ignore
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1090
1267
|
} catch (err) {
|
|
1091
1268
|
const stderr = err?.stderr ?? ''
|
|
1092
1269
|
const missingUtils =
|