create-absolutejs 0.10.2 → 0.11.0
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/dist/constants.d.ts +2 -2
- package/dist/constants.js +2 -2
- package/dist/data.d.ts +10 -10
- package/dist/data.js +104 -95
- package/dist/generators/angular/generateAngularPage.d.ts +7 -0
- package/dist/generators/angular/generateAngularPage.js +175 -0
- package/dist/generators/angular/scaffoldAngular.d.ts +2 -0
- package/dist/generators/angular/scaffoldAngular.js +34 -0
- package/dist/generators/configurations/generateEslintConfig.d.ts +2 -0
- package/dist/generators/configurations/generateEslintConfig.js +253 -0
- package/dist/generators/configurations/generatePackageJson.js +26 -3
- package/dist/generators/configurations/scaffoldConfigurationFiles.js +28 -3
- package/dist/generators/db/dockerInitTemplates.d.ts +9 -9
- package/dist/generators/db/dockerInitTemplates.js +9 -9
- package/dist/generators/db/generateDatabaseTypes.js +5 -0
- package/dist/generators/db/generateDockerContainer.js +4 -2
- package/dist/generators/db/generateDrizzleSchema.js +16 -6
- package/dist/generators/db/handlerTemplates.d.ts +5 -0
- package/dist/generators/db/handlerTemplates.js +6 -0
- package/dist/generators/db/scaffoldDocker.js +42 -30
- package/dist/generators/html/generateHTMLPage.d.ts +1 -1
- package/dist/generators/html/generateHTMLPage.js +2 -2
- package/dist/generators/html/scaffoldHTML.d.ts +1 -1
- package/dist/generators/html/scaffoldHTML.js +2 -2
- package/dist/generators/htmx/generateHTMXPage.d.ts +1 -1
- package/dist/generators/htmx/generateHTMXPage.js +2 -2
- package/dist/generators/htmx/scaffoldHTMX.d.ts +1 -1
- package/dist/generators/htmx/scaffoldHTMX.js +2 -2
- package/dist/generators/project/computeFlags.d.ts +1 -0
- package/dist/generators/project/computeFlags.js +1 -0
- package/dist/generators/project/generateBuildBlock.d.ts +2 -1
- package/dist/generators/project/generateBuildBlock.js +11 -7
- package/dist/generators/project/generateDBBlock.js +6 -0
- package/dist/generators/project/generateImportsBlock.js +39 -27
- package/dist/generators/project/generateMarkupCSS.js +4 -0
- package/dist/generators/project/generateRoutesBlock.d.ts +1 -2
- package/dist/generators/project/generateRoutesBlock.js +28 -17
- package/dist/generators/project/generateServer.js +5 -10
- package/dist/generators/project/scaffoldFrontends.js +20 -1
- package/dist/generators/react/generateReactComponents.d.ts +2 -2
- package/dist/generators/react/generateReactComponents.js +33 -33
- package/dist/generators/react/scaffoldReact.d.ts +1 -1
- package/dist/generators/react/scaffoldReact.js +2 -2
- package/dist/generators/svelte/generateSveltePage.d.ts +1 -1
- package/dist/generators/svelte/generateSveltePage.js +20 -2
- package/dist/generators/svelte/scaffoldSvelte.d.ts +1 -1
- package/dist/generators/svelte/scaffoldSvelte.js +2 -2
- package/dist/generators/vue/generateVuePage.d.ts +1 -1
- package/dist/generators/vue/generateVuePage.js +20 -2
- package/dist/generators/vue/scaffoldVue.d.ts +1 -1
- package/dist/generators/vue/scaffoldVue.js +2 -2
- package/dist/questions/databaseEngine.d.ts +1 -1
- package/dist/questions/frontendDirectoryConfigurations.d.ts +1 -1
- package/dist/questions/frontends.d.ts +1 -1
- package/dist/questions/frontends.js +3 -3
- package/dist/scaffold.js +14 -2
- package/dist/templates/assets/svg/angular.svg +18 -0
- package/dist/templates/configurations/{eslint.config.mjs → eslint.config.example.mjs} +10 -10
- package/dist/templates/configurations/tsconfig.example.json +12 -12
- package/dist/templates/react/components/App.tsx +2 -2
- package/dist/templates/react/components/Head.tsx +7 -7
- package/dist/templates/react/components/OAuthLink.tsx +2 -2
- package/dist/templates/styles/colors.ts +6 -8
- package/dist/templates/styles/reset.css +15 -0
- package/dist/templates/svelte/components/Counter.svelte +4 -0
- package/dist/templates/vue/components/CountButton.vue +1 -1
- package/dist/typeGuards.d.ts +6 -6
- package/dist/typeGuards.js +6 -6
- package/dist/types.d.ts +1 -0
- package/dist/utils/checkDockerInstalled.d.ts +4 -4
- package/dist/utils/checkDockerInstalled.js +78 -71
- package/dist/utils/parseCommandLineOptions.js +13 -16
- package/dist/versions.d.ts +45 -33
- package/dist/versions.js +55 -42
- package/package.json +10 -9
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
export const generateEslintConfig = (frontends) => {
|
|
2
|
+
const hasReact = frontends.includes('react');
|
|
3
|
+
const reactImports = hasReact
|
|
4
|
+
? `import jsxA11yPlugin from 'eslint-plugin-jsx-a11y';
|
|
5
|
+
import reactPlugin from 'eslint-plugin-react';
|
|
6
|
+
import reactCompilerPlugin from 'eslint-plugin-react-compiler';
|
|
7
|
+
import reactHooksPlugin from 'eslint-plugin-react-hooks';
|
|
8
|
+
`
|
|
9
|
+
: '';
|
|
10
|
+
const reactBlock = hasReact
|
|
11
|
+
? ` {
|
|
12
|
+
files: ['example/**/*.{js,jsx,ts,tsx}'],
|
|
13
|
+
plugins: {
|
|
14
|
+
'jsx-a11y': fixupPluginRules(jsxA11yPlugin),
|
|
15
|
+
react: fixupPluginRules(reactPlugin),
|
|
16
|
+
'react-compiler': reactCompilerPlugin,
|
|
17
|
+
'react-hooks': reactHooksPlugin
|
|
18
|
+
},
|
|
19
|
+
rules: {
|
|
20
|
+
'jsx-a11y/prefer-tag-over-role': 'error',
|
|
21
|
+
'react-compiler/react-compiler': 'error',
|
|
22
|
+
'react-hooks/exhaustive-deps': 'warn',
|
|
23
|
+
'react-hooks/rules-of-hooks': 'error',
|
|
24
|
+
'react/checked-requires-onchange-or-readonly': 'error',
|
|
25
|
+
'react/destructuring-assignment': ['error', 'always'],
|
|
26
|
+
'react/jsx-filename-extension': ['error', { extensions: ['.tsx'] }],
|
|
27
|
+
'react/jsx-no-leaked-render': 'error',
|
|
28
|
+
'react/jsx-no-target-blank': 'error',
|
|
29
|
+
'react/jsx-no-useless-fragment': 'error',
|
|
30
|
+
'react/jsx-pascal-case': ['error', { allowAllCaps: true }],
|
|
31
|
+
'react/no-multi-comp': 'error',
|
|
32
|
+
'react/no-unknown-property': 'off',
|
|
33
|
+
'react/react-in-jsx-scope': 'off',
|
|
34
|
+
'react/self-closing-comp': 'error'
|
|
35
|
+
},
|
|
36
|
+
settings: {
|
|
37
|
+
react: { version: 'detect' }
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
`
|
|
41
|
+
: '';
|
|
42
|
+
return `// eslint.config.mjs
|
|
43
|
+
import { dirname } from 'path';
|
|
44
|
+
import { fileURLToPath } from 'url';
|
|
45
|
+
import { fixupPluginRules } from '@eslint/compat';
|
|
46
|
+
import pluginJs from '@eslint/js';
|
|
47
|
+
import stylistic from '@stylistic/eslint-plugin';
|
|
48
|
+
import tsParser from '@typescript-eslint/parser';
|
|
49
|
+
import { defineConfig } from 'eslint/config';
|
|
50
|
+
import absolutePlugin from 'eslint-plugin-absolute';
|
|
51
|
+
import importPlugin from 'eslint-plugin-import';
|
|
52
|
+
${reactImports}import promisePlugin from 'eslint-plugin-promise';
|
|
53
|
+
import securityPlugin from 'eslint-plugin-security';
|
|
54
|
+
import globals from 'globals';
|
|
55
|
+
import tseslint from 'typescript-eslint';
|
|
56
|
+
|
|
57
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
58
|
+
|
|
59
|
+
export default defineConfig([
|
|
60
|
+
pluginJs.configs.recommended,
|
|
61
|
+
|
|
62
|
+
...tseslint.configs.recommended,
|
|
63
|
+
|
|
64
|
+
{
|
|
65
|
+
files: ['**/*.{ts,tsx}'],
|
|
66
|
+
languageOptions: {
|
|
67
|
+
globals: globals.browser,
|
|
68
|
+
parser: tsParser,
|
|
69
|
+
parserOptions: {
|
|
70
|
+
createDefaultProgram: true,
|
|
71
|
+
project: './tsconfig.json',
|
|
72
|
+
tsconfigRootDir: __dirname
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
{
|
|
78
|
+
files: ['**/*.{ts,tsx}'],
|
|
79
|
+
plugins: { '@stylistic': stylistic },
|
|
80
|
+
rules: {
|
|
81
|
+
'@stylistic/padding-line-between-statements': [
|
|
82
|
+
'error',
|
|
83
|
+
{ blankLine: 'always', next: 'return', prev: '*' }
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
{
|
|
89
|
+
files: ['**/*.{js,mjs,cjs,ts,tsx,jsx}'],
|
|
90
|
+
ignores: ['example/build/**'],
|
|
91
|
+
plugins: {
|
|
92
|
+
absolute: fixupPluginRules(absolutePlugin),
|
|
93
|
+
import: fixupPluginRules(importPlugin),
|
|
94
|
+
promise: fixupPluginRules(promisePlugin),
|
|
95
|
+
security: fixupPluginRules(securityPlugin)
|
|
96
|
+
},
|
|
97
|
+
rules: {
|
|
98
|
+
'absolute/explicit-object-types': 'error',
|
|
99
|
+
'absolute/localize-react-props': 'error',
|
|
100
|
+
'absolute/max-depth-extended': ['error', 1],
|
|
101
|
+
'absolute/max-jsxnesting': ['error', 5],
|
|
102
|
+
'absolute/min-var-length': [
|
|
103
|
+
'error',
|
|
104
|
+
{ allowedVars: ['_', 'id', 'db', 'OK'], minLength: 3 }
|
|
105
|
+
],
|
|
106
|
+
'absolute/no-button-navigation': 'error',
|
|
107
|
+
'absolute/no-explicit-return-type': 'error',
|
|
108
|
+
'absolute/no-inline-prop-types': 'error',
|
|
109
|
+
'absolute/no-multi-style-objects': 'error',
|
|
110
|
+
'absolute/no-nested-jsx-return': 'error',
|
|
111
|
+
'absolute/no-or-none-component': 'error',
|
|
112
|
+
'absolute/no-transition-cssproperties': 'error',
|
|
113
|
+
'absolute/no-unnecessary-div': 'error',
|
|
114
|
+
'absolute/no-unnecessary-key': 'error',
|
|
115
|
+
'absolute/no-useless-function': 'error',
|
|
116
|
+
'absolute/seperate-style-files': 'error',
|
|
117
|
+
'absolute/sort-exports': [
|
|
118
|
+
'error',
|
|
119
|
+
{
|
|
120
|
+
caseSensitive: true,
|
|
121
|
+
natural: true,
|
|
122
|
+
order: 'asc',
|
|
123
|
+
variablesBeforeFunctions: true
|
|
124
|
+
}
|
|
125
|
+
],
|
|
126
|
+
'absolute/sort-keys-fixable': [
|
|
127
|
+
'error',
|
|
128
|
+
{
|
|
129
|
+
caseSensitive: true,
|
|
130
|
+
natural: true,
|
|
131
|
+
order: 'asc',
|
|
132
|
+
variablesBeforeFunctions: true
|
|
133
|
+
}
|
|
134
|
+
],
|
|
135
|
+
'arrow-body-style': ['error', 'as-needed'],
|
|
136
|
+
'consistent-return': 'error',
|
|
137
|
+
eqeqeq: 'error',
|
|
138
|
+
'func-style': [
|
|
139
|
+
'error',
|
|
140
|
+
'expression',
|
|
141
|
+
{ allowArrowFunctions: true }
|
|
142
|
+
],
|
|
143
|
+
'import/no-cycle': 'error',
|
|
144
|
+
'import/no-default-export': 'error',
|
|
145
|
+
'import/no-relative-packages': 'error',
|
|
146
|
+
'import/no-unused-modules': ['error', { missingExports: true }],
|
|
147
|
+
'import/order': ['error', { alphabetize: { order: 'asc' } }],
|
|
148
|
+
'no-await-in-loop': 'error',
|
|
149
|
+
'no-console': ['error', { allow: ['warn', 'error'] }],
|
|
150
|
+
'no-debugger': 'error',
|
|
151
|
+
'no-duplicate-case': 'error',
|
|
152
|
+
'no-duplicate-imports': 'error',
|
|
153
|
+
'no-else-return': 'error',
|
|
154
|
+
'no-empty-function': 'error',
|
|
155
|
+
'no-empty-pattern': 'error',
|
|
156
|
+
'no-empty-static-block': 'error',
|
|
157
|
+
'no-fallthrough': 'error',
|
|
158
|
+
'no-floating-decimal': 'error',
|
|
159
|
+
'no-global-assign': 'error',
|
|
160
|
+
'no-implicit-coercion': 'error',
|
|
161
|
+
'no-implicit-globals': 'error',
|
|
162
|
+
'no-loop-func': 'error',
|
|
163
|
+
'no-magic-numbers': [
|
|
164
|
+
'warn',
|
|
165
|
+
{ detectObjects: false, enforceConst: true, ignore: [0, 1] }
|
|
166
|
+
],
|
|
167
|
+
'no-misleading-character-class': 'error',
|
|
168
|
+
'no-nested-ternary': 'error',
|
|
169
|
+
'no-new-native-nonconstructor': 'error',
|
|
170
|
+
'no-new-wrappers': 'error',
|
|
171
|
+
'no-param-reassign': 'error',
|
|
172
|
+
'no-restricted-imports': [
|
|
173
|
+
'error',
|
|
174
|
+
{
|
|
175
|
+
paths: [
|
|
176
|
+
{
|
|
177
|
+
importNames: ['default'],
|
|
178
|
+
message:
|
|
179
|
+
'Import only named React exports for tree-shaking.',
|
|
180
|
+
name: 'react'
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
importNames: ['default'],
|
|
184
|
+
message: 'Import only the required Bun exports.',
|
|
185
|
+
name: 'bun'
|
|
186
|
+
}
|
|
187
|
+
]
|
|
188
|
+
}
|
|
189
|
+
],
|
|
190
|
+
'no-return-await': 'error',
|
|
191
|
+
'no-shadow': 'error',
|
|
192
|
+
'no-undef': 'error',
|
|
193
|
+
'no-unneeded-ternary': 'error',
|
|
194
|
+
'no-unreachable': 'error',
|
|
195
|
+
'no-useless-assignment': 'error',
|
|
196
|
+
'no-useless-concat': 'error',
|
|
197
|
+
'no-useless-return': 'error',
|
|
198
|
+
'no-var': 'error',
|
|
199
|
+
'prefer-arrow-callback': 'error',
|
|
200
|
+
'prefer-const': 'error',
|
|
201
|
+
'prefer-destructuring': [
|
|
202
|
+
'error',
|
|
203
|
+
{ array: true, object: true },
|
|
204
|
+
{ enforceForRenamedProperties: false }
|
|
205
|
+
],
|
|
206
|
+
'prefer-template': 'error',
|
|
207
|
+
'promise/always-return': 'warn',
|
|
208
|
+
'promise/avoid-new': 'warn',
|
|
209
|
+
'promise/catch-or-return': 'error',
|
|
210
|
+
'promise/no-callback-in-promise': 'warn',
|
|
211
|
+
'promise/no-nesting': 'warn',
|
|
212
|
+
'promise/no-promise-in-callback': 'warn',
|
|
213
|
+
'promise/no-return-wrap': 'error',
|
|
214
|
+
'promise/param-names': 'error'
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
${reactBlock} {
|
|
218
|
+
files: [
|
|
219
|
+
'example/server.ts',
|
|
220
|
+
'example/indexes/*.tsx',
|
|
221
|
+
'example/db/migrate.ts'
|
|
222
|
+
],
|
|
223
|
+
rules: {
|
|
224
|
+
'import/no-unused-modules': 'off'
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
files: ['example/db/migrate.ts', 'example/utils/absoluteAuthConfig.ts'],
|
|
229
|
+
rules: {
|
|
230
|
+
'no-console': 'off'
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
files: ['eslint.config.mjs'],
|
|
235
|
+
rules: {
|
|
236
|
+
'no-magic-numbers': 'off'
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
files: ['eslint.config.mjs'],
|
|
241
|
+
rules: {
|
|
242
|
+
'import/no-default-export': 'off'
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
files: ['example/db/schema.ts'],
|
|
247
|
+
rules: {
|
|
248
|
+
'absolute/explicit-object-types': 'off'
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
]);
|
|
252
|
+
`;
|
|
253
|
+
};
|
|
@@ -52,6 +52,15 @@ export const createPackageJson = async ({ projectName, authOption, plugins, data
|
|
|
52
52
|
for (const dep of eslintReactDependencies)
|
|
53
53
|
packageNames.add(dep.value);
|
|
54
54
|
}
|
|
55
|
+
if (flags.requiresAngular) {
|
|
56
|
+
packageNames.add('@angular/common');
|
|
57
|
+
packageNames.add('@angular/compiler');
|
|
58
|
+
packageNames.add('@angular/compiler-cli');
|
|
59
|
+
packageNames.add('@angular/core');
|
|
60
|
+
packageNames.add('@angular/platform-browser');
|
|
61
|
+
packageNames.add('@angular/platform-server');
|
|
62
|
+
packageNames.add('@angular/ssr');
|
|
63
|
+
}
|
|
55
64
|
if (flags.requiresSvelte)
|
|
56
65
|
packageNames.add('svelte');
|
|
57
66
|
if (flags.requiresSvelte && codeQualityTool === 'eslint+prettier')
|
|
@@ -139,6 +148,20 @@ export const createPackageJson = async ({ projectName, authOption, plugins, data
|
|
|
139
148
|
devDependencies[dep.value] = resolveVersion(dep.value, dep.latestVersion);
|
|
140
149
|
});
|
|
141
150
|
}
|
|
151
|
+
if (flags.requiresAngular) {
|
|
152
|
+
const angularPackages = [
|
|
153
|
+
'@angular/common',
|
|
154
|
+
'@angular/compiler',
|
|
155
|
+
'@angular/compiler-cli',
|
|
156
|
+
'@angular/core',
|
|
157
|
+
'@angular/platform-browser',
|
|
158
|
+
'@angular/platform-server',
|
|
159
|
+
'@angular/ssr'
|
|
160
|
+
];
|
|
161
|
+
angularPackages.forEach((pkg) => {
|
|
162
|
+
dependencies[pkg] = resolveVersion(pkg, versions[pkg]);
|
|
163
|
+
});
|
|
164
|
+
}
|
|
142
165
|
if (flags.requiresSvelte) {
|
|
143
166
|
dependencies['svelte'] = resolveVersion('svelte', versions['svelte']);
|
|
144
167
|
}
|
|
@@ -168,9 +191,9 @@ export const createPackageJson = async ({ projectName, authOption, plugins, data
|
|
|
168
191
|
if (latest)
|
|
169
192
|
s.stop(green('Package versions resolved'));
|
|
170
193
|
const scripts = {
|
|
171
|
-
dev: '
|
|
172
|
-
format: `
|
|
173
|
-
lint: '
|
|
194
|
+
dev: 'absolute dev',
|
|
195
|
+
format: `absolute prettier --write "./**/*.{js,ts,css,json,mjs,md${flags.requiresReact ? ',jsx,tsx' : ''}${flags.requiresSvelte ? ',svelte' : ''}${flags.requiresVue ? ',vue' : ''}${flags.requiresHtml || flags.requiresHtmx ? ',html' : ''}}"`,
|
|
196
|
+
lint: 'absolute eslint',
|
|
174
197
|
test: 'echo "Error: no test specified" && exit 1',
|
|
175
198
|
typecheck: 'bun run tsc --noEmit'
|
|
176
199
|
};
|
|
@@ -1,10 +1,35 @@
|
|
|
1
|
-
import { copyFileSync, writeFileSync } from 'fs';
|
|
1
|
+
import { copyFileSync, readFileSync, writeFileSync } from 'fs';
|
|
2
2
|
import { join } from 'path';
|
|
3
3
|
import { dim, yellow } from 'picocolors';
|
|
4
4
|
import { generateEnv } from './generateEnv';
|
|
5
|
+
import { generateEslintConfig } from './generateEslintConfig';
|
|
5
6
|
import { generatePrettierrc } from './generatePrettierrc';
|
|
6
7
|
export const scaffoldConfigurationFiles = ({ tailwind, templatesDirectory, databaseEngine, envVariables, databaseHost, codeQualityTool, frontends, initializeGitNow, projectName }) => {
|
|
7
|
-
|
|
8
|
+
const hasAngular = frontends.includes('angular');
|
|
9
|
+
if (hasAngular) {
|
|
10
|
+
const templatePath = join(templatesDirectory, 'configurations', 'tsconfig.example.json');
|
|
11
|
+
const raw = readFileSync(templatePath, 'utf-8');
|
|
12
|
+
const stripped = raw
|
|
13
|
+
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
14
|
+
.replace(/\/\/[^\n]*/g, '')
|
|
15
|
+
.replace(/,\s*([}\]])/g, '$1');
|
|
16
|
+
const tsconfig = JSON.parse(stripped);
|
|
17
|
+
tsconfig.compilerOptions['emitDecoratorMetadata'] = true;
|
|
18
|
+
tsconfig.compilerOptions['experimentalDecorators'] = true;
|
|
19
|
+
tsconfig.compilerOptions['useDefineForClassFields'] = false;
|
|
20
|
+
const withAngular = {
|
|
21
|
+
angularCompilerOptions: {
|
|
22
|
+
enableI18nLegacyMessageIdFormat: false,
|
|
23
|
+
strictInjectionParameters: true,
|
|
24
|
+
strictTemplates: true
|
|
25
|
+
},
|
|
26
|
+
...tsconfig
|
|
27
|
+
};
|
|
28
|
+
writeFileSync(join(projectName, 'tsconfig.json'), JSON.stringify(withAngular, null, '\t'));
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
copyFileSync(join(templatesDirectory, 'configurations', 'tsconfig.example.json'), join(projectName, 'tsconfig.json'));
|
|
32
|
+
}
|
|
8
33
|
if (tailwind) {
|
|
9
34
|
copyFileSync(join(templatesDirectory, 'tailwind', 'postcss.config.ts'), join(projectName, 'postcss.config.ts'));
|
|
10
35
|
copyFileSync(join(templatesDirectory, 'tailwind', 'tailwind.config.ts'), join(projectName, 'tailwind.config.ts'));
|
|
@@ -12,7 +37,7 @@ export const scaffoldConfigurationFiles = ({ tailwind, templatesDirectory, datab
|
|
|
12
37
|
if (initializeGitNow)
|
|
13
38
|
copyFileSync(join(templatesDirectory, 'git', 'gitignore'), join(projectName, '.gitignore'));
|
|
14
39
|
if (codeQualityTool === 'eslint+prettier') {
|
|
15
|
-
|
|
40
|
+
writeFileSync(join(projectName, 'eslint.config.mjs'), generateEslintConfig(frontends));
|
|
16
41
|
copyFileSync(join(templatesDirectory, 'configurations', '.prettierignore'), join(projectName, '.prettierignore'));
|
|
17
42
|
const prettierrc = generatePrettierrc(frontends);
|
|
18
43
|
writeFileSync(join(projectName, '.prettierrc.json'), prettierrc);
|
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
export declare const userTables: {
|
|
2
|
-
readonly cockroachdb: "CREATE TABLE IF NOT EXISTS users (\n auth_sub VARCHAR(255) PRIMARY KEY,\n created_at TIMESTAMP NOT NULL DEFAULT NOW(),\n metadata JSONB DEFAULT '{}'::jsonb\n);";
|
|
3
|
-
readonly gel: "create type users {\n create required property auth_sub: str {\n create constraint exclusive;\n };\n\n create required property created_at: datetime {\n set default := datetime_current();\n };\n\n create required property metadata: json {\n set default := to_json('{}');\n };\n};";
|
|
4
|
-
readonly mariadb: "CREATE TABLE IF NOT EXISTS users (\n auth_sub VARCHAR(255) PRIMARY KEY,\n created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n metadata JSON DEFAULT ('{}')\n);";
|
|
5
|
-
readonly mssql: "IF OBJECT_ID('users','U') IS NULL\nBEGIN\n CREATE TABLE users (\n auth_sub NVARCHAR(255) PRIMARY KEY,\n created_at DATETIME2 NOT NULL DEFAULT SYSUTCDATETIME(),\n metadata NVARCHAR(MAX) NULL\n );\nEND;";
|
|
6
|
-
readonly mysql: "CREATE TABLE IF NOT EXISTS users (\n auth_sub VARCHAR(255) PRIMARY KEY,\n created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n metadata JSON DEFAULT (JSON_OBJECT())\n);";
|
|
7
|
-
readonly postgresql: "CREATE TABLE IF NOT EXISTS users (\n auth_sub VARCHAR(255) PRIMARY KEY,\n created_at TIMESTAMP NOT NULL DEFAULT NOW(),\n metadata JSONB DEFAULT '{}'::jsonb\n);";
|
|
8
|
-
readonly singlestore: "CREATE TABLE IF NOT EXISTS users (\n auth_sub VARCHAR(255) PRIMARY KEY,\n created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n metadata JSON DEFAULT ('{}')\n);";
|
|
9
|
-
};
|
|
10
1
|
export declare const countHistoryTables: {
|
|
11
2
|
readonly cockroachdb: "CREATE SEQUENCE IF NOT EXISTS count_history_uid_seq START WITH 1 INCREMENT BY 1;\nCREATE TABLE IF NOT EXISTS count_history (\n uid BIGINT PRIMARY KEY DEFAULT nextval('count_history_uid_seq'),\n count INT NOT NULL,\n created_at TIMESTAMP NOT NULL DEFAULT NOW()\n);";
|
|
12
3
|
readonly gel: "create scalar type CountHistoryUid extending sequence;\ncreate type count_history {\n create required property uid: CountHistoryUid {\n create constraint exclusive;\n set default := sequence_next(introspect CountHistoryUid);\n };\n\n create required property count: int16;\n\n create required property created_at: datetime {\n set default := datetime_current();\n };\n};";
|
|
@@ -50,3 +41,12 @@ export declare const initTemplates: {
|
|
|
50
41
|
readonly wait: "until singlestore -u root -ppassword -e \"SELECT 1\" >/dev/null 2>&1; do sleep 1; done";
|
|
51
42
|
};
|
|
52
43
|
};
|
|
44
|
+
export declare const userTables: {
|
|
45
|
+
readonly cockroachdb: "CREATE TABLE IF NOT EXISTS users (\n auth_sub VARCHAR(255) PRIMARY KEY,\n created_at TIMESTAMP NOT NULL DEFAULT NOW(),\n metadata JSONB DEFAULT '{}'::jsonb\n);";
|
|
46
|
+
readonly gel: "create type users {\n create required property auth_sub: str {\n create constraint exclusive;\n };\n\n create required property created_at: datetime {\n set default := datetime_current();\n };\n\n create required property metadata: json {\n set default := to_json('{}');\n };\n};";
|
|
47
|
+
readonly mariadb: "CREATE TABLE IF NOT EXISTS users (\n auth_sub VARCHAR(255) PRIMARY KEY,\n created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n metadata JSON DEFAULT ('{}')\n);";
|
|
48
|
+
readonly mssql: "IF OBJECT_ID('users','U') IS NULL\nBEGIN\n CREATE TABLE users (\n auth_sub NVARCHAR(255) PRIMARY KEY,\n created_at DATETIME2 NOT NULL DEFAULT SYSUTCDATETIME(),\n metadata NVARCHAR(MAX) NULL\n );\nEND;";
|
|
49
|
+
readonly mysql: "CREATE TABLE IF NOT EXISTS users (\n auth_sub VARCHAR(255) PRIMARY KEY,\n created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n metadata JSON DEFAULT (JSON_OBJECT())\n);";
|
|
50
|
+
readonly postgresql: "CREATE TABLE IF NOT EXISTS users (\n auth_sub VARCHAR(255) PRIMARY KEY,\n created_at TIMESTAMP NOT NULL DEFAULT NOW(),\n metadata JSONB DEFAULT '{}'::jsonb\n);";
|
|
51
|
+
readonly singlestore: "CREATE TABLE IF NOT EXISTS users (\n auth_sub VARCHAR(255) PRIMARY KEY,\n created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n metadata JSON DEFAULT ('{}')\n);";
|
|
52
|
+
};
|
|
@@ -91,15 +91,6 @@ create type count_history {
|
|
|
91
91
|
set default := datetime_current();
|
|
92
92
|
};
|
|
93
93
|
};`;
|
|
94
|
-
export const userTables = {
|
|
95
|
-
cockroachdb: cockroachdbUsers,
|
|
96
|
-
gel: gelUsers,
|
|
97
|
-
mariadb: mariadbUsers,
|
|
98
|
-
mssql: mssqlUsers,
|
|
99
|
-
mysql: mysqlUsers,
|
|
100
|
-
postgresql: postgresqlUsers,
|
|
101
|
-
singlestore: singlestoreUsers
|
|
102
|
-
};
|
|
103
94
|
export const countHistoryTables = {
|
|
104
95
|
cockroachdb: cockroachdbCountHistory,
|
|
105
96
|
gel: gelCountHistory,
|
|
@@ -143,3 +134,12 @@ export const initTemplates = {
|
|
|
143
134
|
wait: 'until singlestore -u root -ppassword -e "SELECT 1" >/dev/null 2>&1; do sleep 1; done'
|
|
144
135
|
}
|
|
145
136
|
};
|
|
137
|
+
export const userTables = {
|
|
138
|
+
cockroachdb: cockroachdbUsers,
|
|
139
|
+
gel: gelUsers,
|
|
140
|
+
mariadb: mariadbUsers,
|
|
141
|
+
mssql: mssqlUsers,
|
|
142
|
+
mysql: mysqlUsers,
|
|
143
|
+
postgresql: postgresqlUsers,
|
|
144
|
+
singlestore: singlestoreUsers
|
|
145
|
+
};
|
|
@@ -34,6 +34,11 @@ export const generateDatabaseTypes = ({ databaseEngine, databaseHost, authOption
|
|
|
34
34
|
dbTypeLine =
|
|
35
35
|
'export type DatabaseType = Mysql2Database<SchemaType>;';
|
|
36
36
|
break;
|
|
37
|
+
case 'mssql':
|
|
38
|
+
dbImport = `import { NodeMssqlDatabase } from 'drizzle-orm/node-mssql';`;
|
|
39
|
+
dbTypeLine =
|
|
40
|
+
'export type DatabaseType = NodeMssqlDatabase<SchemaType>;';
|
|
41
|
+
break;
|
|
37
42
|
case 'postgresql':
|
|
38
43
|
dbImport = `import { BunSQLDatabase } from 'drizzle-orm/bun-sql';`;
|
|
39
44
|
dbTypeLine =
|
|
@@ -104,6 +104,7 @@ const templates = {
|
|
|
104
104
|
test: 'singlestore -u root -ppassword -e "SELECT 1" >/dev/null 2>&1'
|
|
105
105
|
},
|
|
106
106
|
image: 'ghcr.io/singlestore-labs/singlestoredb-dev', // NOTE: No tag specified due to data persistence
|
|
107
|
+
platform: 'linux/amd64', // Required for ARM64 (Apple Silicon); no-op on amd64
|
|
107
108
|
port: '3306:3306',
|
|
108
109
|
volumePath: '/data'
|
|
109
110
|
}
|
|
@@ -114,14 +115,15 @@ export const generateDockerContainer = (databaseEngine) => {
|
|
|
114
115
|
databaseEngine === 'sqlite') {
|
|
115
116
|
throw new Error('Internal type error: Expected a valid local database engine');
|
|
116
117
|
}
|
|
117
|
-
const { command, env, healthcheck, image, port, volumePath } = templates[databaseEngine];
|
|
118
|
+
const { command, env, healthcheck, image, platform, port, volumePath } = templates[databaseEngine];
|
|
118
119
|
const commandLine = command ? `\n command: ${command}` : '';
|
|
120
|
+
const platformLine = platform ? `\n platform: ${platform}` : '';
|
|
119
121
|
const envLines = Object.entries(env)
|
|
120
122
|
.map(([key, value]) => ` ${key}: ${value}`)
|
|
121
123
|
.join('\n');
|
|
122
124
|
return `services:
|
|
123
125
|
db:
|
|
124
|
-
image: ${image}
|
|
126
|
+
image: ${image}${platformLine}
|
|
125
127
|
restart: always
|
|
126
128
|
environment:
|
|
127
129
|
${envLines}
|
|
@@ -15,6 +15,14 @@ const DIALECTS = {
|
|
|
15
15
|
table: 'mysqlTable',
|
|
16
16
|
time: 'timestamp()'
|
|
17
17
|
},
|
|
18
|
+
mssql: {
|
|
19
|
+
builders: ['datetime2', 'int', 'mssqlTable', 'nvarchar', 'json'],
|
|
20
|
+
json: "nvarchar({ length: 'max', mode: 'json' })",
|
|
21
|
+
pkg: 'mssql-core',
|
|
22
|
+
string: 'nvarchar({ length: 255 })',
|
|
23
|
+
table: 'mssqlTable',
|
|
24
|
+
time: 'datetime2()'
|
|
25
|
+
},
|
|
18
26
|
mysql: {
|
|
19
27
|
builders: ['json', 'mysqlTable', 'timestamp', 'varchar', 'int'],
|
|
20
28
|
json: 'json()',
|
|
@@ -51,9 +59,10 @@ const DIALECTS = {
|
|
|
51
59
|
const builder = (expr) => expr.split('(')[0];
|
|
52
60
|
export const generateDrizzleSchema = ({ databaseEngine, authOption }) => {
|
|
53
61
|
const cfg = DIALECTS[databaseEngine];
|
|
54
|
-
const intBuilder = databaseEngine === '
|
|
55
|
-
databaseEngine === '
|
|
56
|
-
databaseEngine === '
|
|
62
|
+
const intBuilder = databaseEngine === 'mariadb' ||
|
|
63
|
+
databaseEngine === 'mssql' ||
|
|
64
|
+
databaseEngine === 'mysql' ||
|
|
65
|
+
databaseEngine === 'singlestore'
|
|
57
66
|
? 'int'
|
|
58
67
|
: 'integer';
|
|
59
68
|
const timeBuilder = builder(cfg.time);
|
|
@@ -68,9 +77,10 @@ export const generateDrizzleSchema = ({ databaseEngine, authOption }) => {
|
|
|
68
77
|
? `import { sql } from 'drizzle-orm';\n`
|
|
69
78
|
: '';
|
|
70
79
|
let uidColumn;
|
|
71
|
-
if (databaseEngine === '
|
|
72
|
-
databaseEngine === '
|
|
73
|
-
databaseEngine === '
|
|
80
|
+
if (databaseEngine === 'mariadb' ||
|
|
81
|
+
databaseEngine === 'mssql' ||
|
|
82
|
+
databaseEngine === 'mysql' ||
|
|
83
|
+
databaseEngine === 'singlestore') {
|
|
74
84
|
uidColumn = `${intBuilder}('uid').primaryKey().autoincrement()`;
|
|
75
85
|
}
|
|
76
86
|
else if (databaseEngine === 'sqlite') {
|
|
@@ -35,6 +35,11 @@ declare const driverConfigurations: {
|
|
|
35
35
|
readonly importLines: "";
|
|
36
36
|
readonly queries: QueryOperations;
|
|
37
37
|
};
|
|
38
|
+
readonly 'mssql:drizzle:local': {
|
|
39
|
+
readonly dbType: "NodeMssqlDatabase<SchemaType>";
|
|
40
|
+
readonly importLines: "import { eq } from 'drizzle-orm'\nimport { schema } from '../../../db/schema'";
|
|
41
|
+
readonly queries: QueryOperations;
|
|
42
|
+
};
|
|
38
43
|
readonly 'mssql:sql:local': {
|
|
39
44
|
readonly dbType: "ConnectionPool";
|
|
40
45
|
readonly importLines: "";
|
|
@@ -353,6 +353,12 @@ import { schema } from '../../../db/schema'`,
|
|
|
353
353
|
importLines: ``,
|
|
354
354
|
queries: mongodbQueryOperations
|
|
355
355
|
},
|
|
356
|
+
'mssql:drizzle:local': {
|
|
357
|
+
dbType: 'NodeMssqlDatabase<SchemaType>',
|
|
358
|
+
importLines: `import { eq } from 'drizzle-orm'
|
|
359
|
+
import { schema } from '../../../db/schema'`,
|
|
360
|
+
queries: drizzleQueryOperations
|
|
361
|
+
},
|
|
356
362
|
'mssql:sql:local': {
|
|
357
363
|
dbType: 'ConnectionPool',
|
|
358
364
|
importLines: ``,
|
|
@@ -6,6 +6,33 @@ import { green, red } from 'picocolors';
|
|
|
6
6
|
import { checkDockerInstalled, ensureDockerDaemonRunning, resolveDockerExe, shutdownDockerDaemon } from '../../utils/checkDockerInstalled';
|
|
7
7
|
import { countHistoryTables, initTemplates, userTables } from './dockerInitTemplates';
|
|
8
8
|
import { generateDockerContainer } from './generateDockerContainer';
|
|
9
|
+
const initDockerSchema = async ({ authOption, databaseEngine, docker, projectName, spin }) => {
|
|
10
|
+
const dbKey = databaseEngine;
|
|
11
|
+
const { wait, cli } = initTemplates[dbKey];
|
|
12
|
+
const usesAuth = authOption !== undefined && authOption !== 'none';
|
|
13
|
+
const dbCommand = usesAuth ? userTables[dbKey] : countHistoryTables[dbKey];
|
|
14
|
+
await $ `${docker} compose -p ${databaseEngine} -f db/docker-compose.db.yml up -d db`
|
|
15
|
+
.cwd(projectName)
|
|
16
|
+
.quiet();
|
|
17
|
+
spin.message(`Initializing ${databaseEngine} schema`);
|
|
18
|
+
await $ `${docker} compose -p ${databaseEngine} -f db/docker-compose.db.yml exec -T db \
|
|
19
|
+
bash -lc '${wait} && ${cli} "${dbCommand}"'`
|
|
20
|
+
.cwd(projectName)
|
|
21
|
+
.quiet();
|
|
22
|
+
spin.message(`Stopping ${databaseEngine} container`);
|
|
23
|
+
await $ `${docker} compose -p ${databaseEngine} -f db/docker-compose.db.yml down`
|
|
24
|
+
.cwd(projectName)
|
|
25
|
+
.quiet();
|
|
26
|
+
};
|
|
27
|
+
const verifyDockerContainer = async ({ databaseEngine, docker, projectName, spin }) => {
|
|
28
|
+
await $ `${docker} compose -p ${databaseEngine} -f db/docker-compose.db.yml up -d --wait db`
|
|
29
|
+
.cwd(projectName)
|
|
30
|
+
.quiet();
|
|
31
|
+
spin.message(`Stopping ${databaseEngine} container`);
|
|
32
|
+
await $ `${docker} compose -p ${databaseEngine} -f db/docker-compose.db.yml down`
|
|
33
|
+
.cwd(projectName)
|
|
34
|
+
.quiet();
|
|
35
|
+
};
|
|
9
36
|
export const scaffoldDocker = async ({ databaseEngine, projectDatabaseDirectory, projectName, authOption }) => {
|
|
10
37
|
if (databaseEngine === undefined ||
|
|
11
38
|
databaseEngine === 'none' ||
|
|
@@ -19,37 +46,22 @@ export const scaffoldDocker = async ({ databaseEngine, projectDatabaseDirectory,
|
|
|
19
46
|
const docker = resolveDockerExe();
|
|
20
47
|
const spin = spinner();
|
|
21
48
|
spin.start(`Starting ${databaseEngine} container`);
|
|
49
|
+
const dockerAction = databaseEngine in userTables
|
|
50
|
+
? () => initDockerSchema({
|
|
51
|
+
authOption,
|
|
52
|
+
databaseEngine,
|
|
53
|
+
docker,
|
|
54
|
+
projectName,
|
|
55
|
+
spin
|
|
56
|
+
})
|
|
57
|
+
: () => verifyDockerContainer({
|
|
58
|
+
databaseEngine,
|
|
59
|
+
docker,
|
|
60
|
+
projectName,
|
|
61
|
+
spin
|
|
62
|
+
});
|
|
22
63
|
try {
|
|
23
|
-
|
|
24
|
-
if (hasSchemaInit) {
|
|
25
|
-
const dbKey = databaseEngine;
|
|
26
|
-
const { wait, cli } = initTemplates[dbKey];
|
|
27
|
-
const usesAuth = authOption !== undefined && authOption !== 'none';
|
|
28
|
-
const dbCommand = usesAuth
|
|
29
|
-
? userTables[dbKey]
|
|
30
|
-
: countHistoryTables[dbKey];
|
|
31
|
-
await $ `${docker} compose -p ${databaseEngine} -f db/docker-compose.db.yml up -d db`
|
|
32
|
-
.cwd(projectName)
|
|
33
|
-
.quiet();
|
|
34
|
-
spin.message(`Initializing ${databaseEngine} schema`);
|
|
35
|
-
await $ `${docker} compose -p ${databaseEngine} -f db/docker-compose.db.yml exec -T db \
|
|
36
|
-
bash -lc '${wait} && ${cli} "${dbCommand}"'`
|
|
37
|
-
.cwd(projectName)
|
|
38
|
-
.quiet();
|
|
39
|
-
spin.message(`Stopping ${databaseEngine} container`);
|
|
40
|
-
await $ `${docker} compose -p ${databaseEngine} -f db/docker-compose.db.yml down`
|
|
41
|
-
.cwd(projectName)
|
|
42
|
-
.quiet();
|
|
43
|
-
}
|
|
44
|
-
else {
|
|
45
|
-
await $ `${docker} compose -p ${databaseEngine} -f db/docker-compose.db.yml up -d --wait db`
|
|
46
|
-
.cwd(projectName)
|
|
47
|
-
.quiet();
|
|
48
|
-
spin.message(`Stopping ${databaseEngine} container`);
|
|
49
|
-
await $ `${docker} compose -p ${databaseEngine} -f db/docker-compose.db.yml down`
|
|
50
|
-
.cwd(projectName)
|
|
51
|
-
.quiet();
|
|
52
|
-
}
|
|
64
|
+
await dockerAction();
|
|
53
65
|
spin.stop(green('Docker container verified'));
|
|
54
66
|
}
|
|
55
67
|
catch (err) {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { CreateConfiguration, Frontend } from '../../types';
|
|
2
|
-
export declare const generateHTMLPage: (frontends: Frontend[], useHTMLScripts: CreateConfiguration["useHTMLScripts"]) => string;
|
|
2
|
+
export declare const generateHTMLPage: (frontends: Frontend[], useHTMLScripts: CreateConfiguration["useHTMLScripts"], editBasePath: string) => string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { formatNavLink } from '../../utils/formatNavLink';
|
|
2
|
-
export const generateHTMLPage = (frontends, useHTMLScripts) => {
|
|
2
|
+
export const generateHTMLPage = (frontends, useHTMLScripts, editBasePath) => {
|
|
3
3
|
const navLinks = frontends.map(formatNavLink).join('\n\t\t\t');
|
|
4
4
|
const initialCount = useHTMLScripts ? '0' : 'disabled';
|
|
5
5
|
const scriptTagBlock = useHTMLScripts
|
|
@@ -54,7 +54,7 @@ export const generateHTMLPage = (frontends, useHTMLScripts) => {
|
|
|
54
54
|
count is <span id="counter">${initialCount}</span>
|
|
55
55
|
</button>
|
|
56
56
|
<p>
|
|
57
|
-
Edit <code
|
|
57
|
+
Edit <code>${editBasePath}/pages/HTMLExample.html</code> and save
|
|
58
58
|
to test HMR.
|
|
59
59
|
</p>
|
|
60
60
|
${frontends.length > 1
|
|
@@ -2,5 +2,5 @@ import { ScaffoldFrontendProps } from '../../types';
|
|
|
2
2
|
type ScaffoldHTMLProps = ScaffoldFrontendProps & {
|
|
3
3
|
useHTMLScripts: boolean;
|
|
4
4
|
};
|
|
5
|
-
export declare const scaffoldHTML: ({ isSingleFrontend, targetDirectory, frontends, useHTMLScripts, templatesDirectory, projectAssetsDirectory }: ScaffoldHTMLProps) => void;
|
|
5
|
+
export declare const scaffoldHTML: ({ editBasePath, isSingleFrontend, targetDirectory, frontends, useHTMLScripts, templatesDirectory, projectAssetsDirectory }: ScaffoldHTMLProps) => void;
|
|
6
6
|
export {};
|
|
@@ -2,9 +2,9 @@ import { copyFileSync, cpSync, mkdirSync, writeFileSync } from 'fs';
|
|
|
2
2
|
import { join } from 'path';
|
|
3
3
|
import { generateMarkupCSS } from '../project/generateMarkupCSS';
|
|
4
4
|
import { generateHTMLPage } from './generateHTMLPage';
|
|
5
|
-
export const scaffoldHTML = ({ isSingleFrontend, targetDirectory, frontends, useHTMLScripts, templatesDirectory, projectAssetsDirectory }) => {
|
|
5
|
+
export const scaffoldHTML = ({ editBasePath, isSingleFrontend, targetDirectory, frontends, useHTMLScripts, templatesDirectory, projectAssetsDirectory }) => {
|
|
6
6
|
copyFileSync(join(templatesDirectory, 'assets', 'svg', 'HTML5_Badge.svg'), join(projectAssetsDirectory, 'svg', 'HTML5_Badge.svg'));
|
|
7
|
-
const htmlPage = generateHTMLPage(frontends, useHTMLScripts);
|
|
7
|
+
const htmlPage = generateHTMLPage(frontends, useHTMLScripts, editBasePath);
|
|
8
8
|
const pagesDirectory = join(targetDirectory, 'pages');
|
|
9
9
|
mkdirSync(pagesDirectory, { recursive: true });
|
|
10
10
|
const htmlFilePath = join(pagesDirectory, 'HTMLExample.html');
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { Frontend } from '../../types';
|
|
2
|
-
export declare const generateHTMXPage: (isSingle: boolean, frontends: Frontend[]) => string;
|
|
2
|
+
export declare const generateHTMXPage: (isSingle: boolean, frontends: Frontend[], editBasePath: string) => string;
|