on-zero 0.1.22 → 0.1.24
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/cjs/cli.cjs +17 -424
- package/dist/cjs/cli.js +7 -402
- package/dist/cjs/cli.js.map +2 -2
- package/dist/cjs/cli.native.js +15 -519
- package/dist/cjs/cli.native.js.map +1 -1
- package/dist/cjs/generate.cjs +370 -0
- package/dist/cjs/generate.js +339 -0
- package/dist/cjs/generate.js.map +6 -0
- package/dist/cjs/generate.native.js +464 -0
- package/dist/cjs/generate.native.js.map +1 -0
- package/dist/cjs/generate.test.cjs +113 -0
- package/dist/cjs/generate.test.js +126 -0
- package/dist/cjs/generate.test.js.map +6 -0
- package/dist/cjs/generate.test.native.js +116 -0
- package/dist/cjs/generate.test.native.js.map +1 -0
- package/dist/cjs/vite-plugin.cjs +37 -121
- package/dist/cjs/vite-plugin.js +41 -100
- package/dist/cjs/vite-plugin.js.map +1 -1
- package/dist/cjs/vite-plugin.native.js +47 -157
- package/dist/cjs/vite-plugin.native.js.map +1 -1
- package/dist/esm/cli.js +8 -388
- package/dist/esm/cli.js.map +2 -2
- package/dist/esm/cli.mjs +17 -402
- package/dist/esm/cli.mjs.map +1 -1
- package/dist/esm/cli.native.js +15 -497
- package/dist/esm/cli.native.js.map +1 -1
- package/dist/esm/generate.js +317 -0
- package/dist/esm/generate.js.map +6 -0
- package/dist/esm/generate.mjs +335 -0
- package/dist/esm/generate.mjs.map +1 -0
- package/dist/esm/generate.native.js +426 -0
- package/dist/esm/generate.native.js.map +1 -0
- package/dist/esm/generate.test.js +130 -0
- package/dist/esm/generate.test.js.map +6 -0
- package/dist/esm/generate.test.mjs +114 -0
- package/dist/esm/generate.test.mjs.map +1 -0
- package/dist/esm/generate.test.native.js +114 -0
- package/dist/esm/generate.test.native.js.map +1 -0
- package/dist/esm/vite-plugin.js +42 -102
- package/dist/esm/vite-plugin.js.map +1 -1
- package/dist/esm/vite-plugin.mjs +37 -121
- package/dist/esm/vite-plugin.mjs.map +1 -1
- package/dist/esm/vite-plugin.native.js +47 -157
- package/dist/esm/vite-plugin.native.js.map +1 -1
- package/package.json +6 -3
- package/readme.md +0 -29
- package/src/cli.ts +9 -646
- package/src/generate.test.ts +201 -0
- package/src/generate.ts +491 -0
- package/src/vite-plugin.ts +61 -189
- package/types/generate.d.ts +21 -0
- package/types/generate.d.ts.map +1 -0
- package/types/generate.test.d.ts +2 -0
- package/types/generate.test.d.ts.map +1 -0
- package/types/vite-plugin.d.ts +6 -29
- package/types/vite-plugin.d.ts.map +1 -1
package/src/vite-plugin.ts
CHANGED
|
@@ -1,238 +1,110 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
3
|
-
import { basename, resolve } from 'node:path'
|
|
1
|
+
import { isAbsolute, relative, resolve } from 'node:path'
|
|
4
2
|
|
|
5
|
-
import type
|
|
6
|
-
|
|
7
|
-
const hash = (s: string) => createHash('sha256').update(s).digest('hex')
|
|
8
|
-
|
|
9
|
-
// cache to avoid unnecessary writes
|
|
10
|
-
let generateCache: Record<string, string> = {}
|
|
11
|
-
|
|
12
|
-
function writeFileIfChanged(filePath: string, content: string): boolean {
|
|
13
|
-
const contentHash = hash(content)
|
|
14
|
-
if (generateCache[filePath] === contentHash && existsSync(filePath)) {
|
|
15
|
-
return false
|
|
16
|
-
}
|
|
17
|
-
writeFileSync(filePath, content, 'utf-8')
|
|
18
|
-
generateCache[filePath] = contentHash
|
|
19
|
-
return true
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function generateModelsFile(modelFiles: string[]) {
|
|
23
|
-
const modelNames = modelFiles.map((f) => basename(f, '.ts')).sort()
|
|
24
|
-
const getImportName = (name: string) => (name === 'user' ? 'userPublic' : name)
|
|
25
|
-
|
|
26
|
-
const imports = modelNames
|
|
27
|
-
.map((name) => `import * as ${getImportName(name)} from '../models/${name}'`)
|
|
28
|
-
.join('\n')
|
|
29
|
-
|
|
30
|
-
const sortedByImportName = [...modelNames].sort((a, b) =>
|
|
31
|
-
getImportName(a).localeCompare(getImportName(b))
|
|
32
|
-
)
|
|
33
|
-
const modelsObj = `export const models = {\n${sortedByImportName.map((name) => ` ${getImportName(name)},`).join('\n')}\n}`
|
|
34
|
-
|
|
35
|
-
const hmrBoundary = `
|
|
36
|
-
if (import.meta.hot) {
|
|
37
|
-
import.meta.hot.accept()
|
|
38
|
-
}
|
|
39
|
-
`
|
|
40
|
-
|
|
41
|
-
return `// auto-generated by on-zero vite plugin\n${imports}\n\n${modelsObj}\n${hmrBoundary}`
|
|
42
|
-
}
|
|
3
|
+
import { generate, type GenerateOptions } from './generate'
|
|
43
4
|
|
|
44
|
-
|
|
45
|
-
const modelNames = modelFiles.map((f) => basename(f, '.ts')).sort()
|
|
46
|
-
const getSchemaName = (name: string) => (name === 'user' ? 'userPublic' : name)
|
|
47
|
-
|
|
48
|
-
const typeExports = modelNames
|
|
49
|
-
.map((name) => {
|
|
50
|
-
const pascalName = name.charAt(0).toUpperCase() + name.slice(1)
|
|
51
|
-
const schemaName = getSchemaName(name)
|
|
52
|
-
return `export type ${pascalName} = TableInsertRow<typeof schema.${schemaName}>\nexport type ${pascalName}Update = TableUpdateRow<typeof schema.${schemaName}>`
|
|
53
|
-
})
|
|
54
|
-
.join('\n\n')
|
|
55
|
-
|
|
56
|
-
return `import type { TableInsertRow, TableUpdateRow } from 'on-zero'\nimport type * as schema from './tables'\n\n${typeExports}\n`
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function generateTablesFile(modelFiles: string[]) {
|
|
60
|
-
const modelNames = modelFiles.map((f) => basename(f, '.ts')).sort()
|
|
61
|
-
const getExportName = (name: string) => (name === 'user' ? 'userPublic' : name)
|
|
62
|
-
|
|
63
|
-
const exports = modelNames
|
|
64
|
-
.map((name) => `export { schema as ${getExportName(name)} } from '../models/${name}'`)
|
|
65
|
-
.join('\n')
|
|
5
|
+
import type { Plugin } from 'vite'
|
|
66
6
|
|
|
67
|
-
|
|
7
|
+
export interface OnZeroPluginOptions extends Omit<GenerateOptions, 'dir' | 'silent'> {
|
|
8
|
+
/** base data directory. defaults to src/data */
|
|
9
|
+
dir?: string
|
|
10
|
+
/** additional paths to apply HMR fix to */
|
|
11
|
+
hmrInclude?: string[]
|
|
12
|
+
/** disable code generation (HMR only) */
|
|
13
|
+
disableGenerate?: boolean
|
|
68
14
|
}
|
|
69
15
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
writeFileIfChanged(
|
|
91
|
-
resolve(generatedDir, 'models.ts'),
|
|
92
|
-
generateModelsFile(allModelFiles)
|
|
93
|
-
),
|
|
94
|
-
writeFileIfChanged(
|
|
95
|
-
resolve(generatedDir, 'types.ts'),
|
|
96
|
-
generateTypesFile(filesWithSchema)
|
|
97
|
-
),
|
|
98
|
-
writeFileIfChanged(
|
|
99
|
-
resolve(generatedDir, 'tables.ts'),
|
|
100
|
-
generateTablesFile(filesWithSchema)
|
|
101
|
-
),
|
|
102
|
-
]
|
|
103
|
-
|
|
104
|
-
const filesChanged = writeResults.filter(Boolean).length
|
|
105
|
-
if (filesChanged > 0 && !silent) {
|
|
106
|
-
console.info(`[on-zero] generated ${filesChanged} file(s)`)
|
|
16
|
+
function createOnZeroHmrPlugin(hmrInclude: string[] = []): Plugin {
|
|
17
|
+
const hmrPaths = ['/models/', '/generated/', '/queries/', ...hmrInclude]
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
name: 'on-zero:hmr',
|
|
21
|
+
apply: 'serve',
|
|
22
|
+
enforce: 'post',
|
|
23
|
+
|
|
24
|
+
transform(code, id) {
|
|
25
|
+
if (!hmrPaths.some((p) => id.includes(p)) || !/\.tsx?$/.test(id)) return
|
|
26
|
+
if (!code.includes('import.meta.hot.invalidate')) return
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
code: code.replace(
|
|
30
|
+
/if\s*\(invalidateMessage\)\s*import\.meta\.hot\.invalidate\(invalidateMessage\);?/g,
|
|
31
|
+
'/* on-zero: HMR invalidate disabled */'
|
|
32
|
+
),
|
|
33
|
+
map: null,
|
|
34
|
+
}
|
|
35
|
+
},
|
|
107
36
|
}
|
|
108
|
-
|
|
109
|
-
return filesChanged
|
|
110
37
|
}
|
|
111
38
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
* @default 'src/data'
|
|
116
|
-
*/
|
|
117
|
-
dataDir?: string
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Additional paths to apply HMR fix to
|
|
121
|
-
*/
|
|
122
|
-
hmrInclude?: string[]
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Disable code generation (HMR only)
|
|
126
|
-
*/
|
|
127
|
-
disableGenerate?: boolean
|
|
39
|
+
function isWithinDirectory(file: string, dir: string): boolean {
|
|
40
|
+
const rel = relative(dir, file)
|
|
41
|
+
return rel !== '' && !rel.startsWith('..') && !isAbsolute(rel)
|
|
128
42
|
}
|
|
129
43
|
|
|
130
|
-
/**
|
|
131
|
-
* Vite plugin for on-zero that handles:
|
|
132
|
-
* 1. Code generation (models.ts, types.ts, tables.ts)
|
|
133
|
-
* 2. HMR support for model files (prevents cascade, enables hot-swap)
|
|
134
|
-
*
|
|
135
|
-
* @example
|
|
136
|
-
* ```ts
|
|
137
|
-
* import { onZeroPlugin } from 'on-zero/vite'
|
|
138
|
-
*
|
|
139
|
-
* export default {
|
|
140
|
-
* plugins: [
|
|
141
|
-
* onZeroPlugin(),
|
|
142
|
-
* // ... other plugins
|
|
143
|
-
* ]
|
|
144
|
-
* }
|
|
145
|
-
* ```
|
|
146
|
-
*/
|
|
147
44
|
export function onZeroPlugin(options: OnZeroPluginOptions = {}): Plugin[] {
|
|
148
|
-
const
|
|
149
|
-
const hmrPaths = ['/models/', '/generated/', ...(options.hmrInclude || [])]
|
|
45
|
+
const dir = options.dir ?? 'src/data'
|
|
150
46
|
|
|
47
|
+
let dataDir: string
|
|
151
48
|
let modelsDir: string
|
|
152
|
-
let
|
|
49
|
+
let queriesDir: string
|
|
50
|
+
|
|
51
|
+
const runGenerate = (silent: boolean) =>
|
|
52
|
+
generate({
|
|
53
|
+
dir: dataDir,
|
|
54
|
+
after: options.after,
|
|
55
|
+
silent,
|
|
56
|
+
})
|
|
153
57
|
|
|
154
58
|
return [
|
|
155
|
-
// generation plugin
|
|
156
59
|
{
|
|
157
|
-
name: 'on-zero:
|
|
60
|
+
name: 'on-zero:serve',
|
|
158
61
|
apply: 'serve',
|
|
159
62
|
|
|
160
63
|
configResolved(config) {
|
|
161
|
-
|
|
162
|
-
|
|
64
|
+
dataDir = resolve(config.root, dir)
|
|
65
|
+
modelsDir = resolve(dataDir, 'models')
|
|
66
|
+
queriesDir = resolve(dataDir, 'queries')
|
|
163
67
|
},
|
|
164
68
|
|
|
165
69
|
async buildStart() {
|
|
166
|
-
if (options.disableGenerate)
|
|
167
|
-
await runGenerate({ modelsDir, generatedDir, silent: false })
|
|
70
|
+
if (!options.disableGenerate) await runGenerate(false)
|
|
168
71
|
},
|
|
169
72
|
|
|
170
73
|
configureServer(server) {
|
|
171
74
|
if (options.disableGenerate) return
|
|
172
75
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
if (file
|
|
176
|
-
await runGenerate(
|
|
177
|
-
}
|
|
178
|
-
})
|
|
179
|
-
|
|
180
|
-
server.watcher.on('add', async (file) => {
|
|
181
|
-
if (file.includes(modelsDir)) {
|
|
182
|
-
await runGenerate({ modelsDir, generatedDir, silent: false })
|
|
183
|
-
}
|
|
184
|
-
})
|
|
185
|
-
|
|
186
|
-
server.watcher.on('unlink', async (file) => {
|
|
187
|
-
if (file.includes(modelsDir)) {
|
|
188
|
-
await runGenerate({ modelsDir, generatedDir, silent: false })
|
|
189
|
-
}
|
|
190
|
-
})
|
|
191
|
-
},
|
|
192
|
-
},
|
|
193
|
-
|
|
194
|
-
// HMR plugin - removes invalidate calls to prevent cascade
|
|
195
|
-
{
|
|
196
|
-
name: 'on-zero:hmr',
|
|
197
|
-
apply: 'serve',
|
|
198
|
-
enforce: 'post',
|
|
199
|
-
|
|
200
|
-
transform(code, id) {
|
|
201
|
-
const shouldTransform = hmrPaths.some((p) => id.includes(p)) && /\.tsx?$/.test(id)
|
|
202
|
-
|
|
203
|
-
if (shouldTransform && code.includes('import.meta.hot.invalidate')) {
|
|
204
|
-
return {
|
|
205
|
-
code: code.replace(
|
|
206
|
-
/if\s*\(invalidateMessage\)\s*import\.meta\.hot\.invalidate\(invalidateMessage\);?/g,
|
|
207
|
-
'/* on-zero: HMR invalidate disabled */'
|
|
208
|
-
),
|
|
209
|
-
map: null,
|
|
76
|
+
const handler = async (file: string) => {
|
|
77
|
+
if (!/\.tsx?$/.test(file)) return
|
|
78
|
+
if (isWithinDirectory(file, modelsDir) || isWithinDirectory(file, queriesDir)) {
|
|
79
|
+
await runGenerate(false)
|
|
210
80
|
}
|
|
211
81
|
}
|
|
82
|
+
|
|
83
|
+
server.watcher.on('change', handler)
|
|
84
|
+
server.watcher.on('add', handler)
|
|
85
|
+
server.watcher.on('unlink', handler)
|
|
212
86
|
},
|
|
213
87
|
},
|
|
214
88
|
|
|
215
|
-
// build plugin - generate once before build
|
|
216
89
|
{
|
|
217
90
|
name: 'on-zero:build',
|
|
218
91
|
apply: 'build',
|
|
219
92
|
|
|
220
93
|
configResolved(config) {
|
|
221
|
-
|
|
222
|
-
generatedDir = resolve(config.root, dataDir, 'generated')
|
|
94
|
+
dataDir = resolve(config.root, dir)
|
|
223
95
|
},
|
|
224
96
|
|
|
225
97
|
async buildStart() {
|
|
226
|
-
if (options.disableGenerate)
|
|
227
|
-
await runGenerate({ modelsDir, generatedDir, silent: true })
|
|
98
|
+
if (!options.disableGenerate) await runGenerate(true)
|
|
228
99
|
},
|
|
229
100
|
},
|
|
101
|
+
|
|
102
|
+
createOnZeroHmrPlugin(options.hmrInclude),
|
|
230
103
|
]
|
|
231
104
|
}
|
|
232
105
|
|
|
233
|
-
// legacy export for backwards compat
|
|
234
106
|
export const onZeroHmrPlugin = (options?: { include?: string[] }): Plugin => {
|
|
235
|
-
return
|
|
107
|
+
return createOnZeroHmrPlugin(options?.include)
|
|
236
108
|
}
|
|
237
109
|
|
|
238
110
|
export default onZeroPlugin
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface GenerateOptions {
|
|
2
|
+
/** base data directory */
|
|
3
|
+
dir: string;
|
|
4
|
+
/** run after generation */
|
|
5
|
+
after?: string;
|
|
6
|
+
/** suppress output */
|
|
7
|
+
silent?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface WatchOptions extends GenerateOptions {
|
|
10
|
+
/** debounce delay in ms */
|
|
11
|
+
debounce?: number;
|
|
12
|
+
}
|
|
13
|
+
export interface GenerateResult {
|
|
14
|
+
filesChanged: number;
|
|
15
|
+
modelCount: number;
|
|
16
|
+
schemaCount: number;
|
|
17
|
+
queryCount: number;
|
|
18
|
+
}
|
|
19
|
+
export declare function generate(options: GenerateOptions): Promise<GenerateResult>;
|
|
20
|
+
export declare function watch(options: WatchOptions): Promise<import("chokidar").FSWatcher>;
|
|
21
|
+
//# sourceMappingURL=generate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"AAsRA,MAAM,WAAW,eAAe;IAC9B,0BAA0B;IAC1B,GAAG,EAAE,MAAM,CAAA;IACX,2BAA2B;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,sBAAsB;IACtB,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,WAAW,YAAa,SAAQ,eAAe;IACnD,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,wBAAsB,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CA2JhF;AAED,wBAAsB,KAAK,CAAC,OAAO,EAAE,YAAY,yCAkChD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.test.d.ts","sourceRoot":"","sources":["../src/generate.test.ts"],"names":[],"mappings":""}
|
package/types/vite-plugin.d.ts
CHANGED
|
@@ -1,36 +1,13 @@
|
|
|
1
|
+
import { type GenerateOptions } from './generate';
|
|
1
2
|
import type { Plugin } from 'vite';
|
|
2
|
-
export interface OnZeroPluginOptions {
|
|
3
|
-
/**
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
*/
|
|
7
|
-
dataDir?: string;
|
|
8
|
-
/**
|
|
9
|
-
* Additional paths to apply HMR fix to
|
|
10
|
-
*/
|
|
3
|
+
export interface OnZeroPluginOptions extends Omit<GenerateOptions, 'dir' | 'silent'> {
|
|
4
|
+
/** base data directory. defaults to src/data */
|
|
5
|
+
dir?: string;
|
|
6
|
+
/** additional paths to apply HMR fix to */
|
|
11
7
|
hmrInclude?: string[];
|
|
12
|
-
/**
|
|
13
|
-
* Disable code generation (HMR only)
|
|
14
|
-
*/
|
|
8
|
+
/** disable code generation (HMR only) */
|
|
15
9
|
disableGenerate?: boolean;
|
|
16
10
|
}
|
|
17
|
-
/**
|
|
18
|
-
* Vite plugin for on-zero that handles:
|
|
19
|
-
* 1. Code generation (models.ts, types.ts, tables.ts)
|
|
20
|
-
* 2. HMR support for model files (prevents cascade, enables hot-swap)
|
|
21
|
-
*
|
|
22
|
-
* @example
|
|
23
|
-
* ```ts
|
|
24
|
-
* import { onZeroPlugin } from 'on-zero/vite'
|
|
25
|
-
*
|
|
26
|
-
* export default {
|
|
27
|
-
* plugins: [
|
|
28
|
-
* onZeroPlugin(),
|
|
29
|
-
* // ... other plugins
|
|
30
|
-
* ]
|
|
31
|
-
* }
|
|
32
|
-
* ```
|
|
33
|
-
*/
|
|
34
11
|
export declare function onZeroPlugin(options?: OnZeroPluginOptions): Plugin[];
|
|
35
12
|
export declare const onZeroHmrPlugin: (options?: {
|
|
36
13
|
include?: string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite-plugin.d.ts","sourceRoot":"","sources":["../src/vite-plugin.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"vite-plugin.d.ts","sourceRoot":"","sources":["../src/vite-plugin.ts"],"names":[],"mappings":"AAEA,OAAO,EAAY,KAAK,eAAe,EAAE,MAAM,YAAY,CAAA;AAE3D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAElC,MAAM,WAAW,mBAAoB,SAAQ,IAAI,CAAC,eAAe,EAAE,KAAK,GAAG,QAAQ,CAAC;IAClF,gDAAgD;IAChD,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,2CAA2C;IAC3C,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;IACrB,yCAAyC;IACzC,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B;AA8BD,wBAAgB,YAAY,CAAC,OAAO,GAAE,mBAAwB,GAAG,MAAM,EAAE,CA4DxE;AAED,eAAO,MAAM,eAAe,GAAI,UAAU;IAAE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,KAAG,MAElE,CAAA;AAED,eAAe,YAAY,CAAA"}
|