@voxgig/create-sdkgen 0.30.9 → 0.31.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/README.md +12 -1
- package/bin/create-sdkgen +25 -2
- package/dist/copymerge.d.ts +35 -0
- package/dist/copymerge.js +510 -0
- package/dist/copymerge.js.map +1 -0
- package/dist/create-sdkgen.d.ts +2 -0
- package/dist/create-sdkgen.js +30 -1
- package/dist/create-sdkgen.js.map +1 -1
- package/dist/project/standard/CreateRoot.d.ts +2 -1
- package/dist/project/standard/CreateRoot.js +8 -1
- package/dist/project/standard/CreateRoot.js.map +1 -1
- package/dist/scaffold-record.d.ts +9 -0
- package/dist/scaffold-record.js +92 -0
- package/dist/scaffold-record.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/project/standard/.sdk/package.json +3 -3
- package/src/copymerge.ts +711 -0
- package/src/create-sdkgen.ts +43 -1
- package/src/project/standard/CreateRoot.ts +7 -0
- package/src/scaffold-record.ts +119 -0
package/src/create-sdkgen.ts
CHANGED
|
@@ -13,6 +13,13 @@ import Pkg from '../package.json'
|
|
|
13
13
|
|
|
14
14
|
import { TARGETS, forwardingError } from './args'
|
|
15
15
|
|
|
16
|
+
import { gitVersions, withCopyMerge } from './copymerge'
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
SCAFFOLD_BASE, SCAFFOLD_CONFLICT,
|
|
20
|
+
fingerprint, scaffoldMode, readScaffold, recordScaffold,
|
|
21
|
+
} from './scaffold-record'
|
|
22
|
+
|
|
16
23
|
|
|
17
24
|
type FullCreateSdkGenOptions = {
|
|
18
25
|
fs?: any
|
|
@@ -36,6 +43,8 @@ type GenerateSpec = {
|
|
|
36
43
|
target?: string[]
|
|
37
44
|
feature?: string[]
|
|
38
45
|
install?: boolean // perform installation - run npm install
|
|
46
|
+
force?: boolean // take the incoming scaffold over local changes
|
|
47
|
+
keep?: boolean // keep local changes whose merge stops
|
|
39
48
|
folder?: string // Target folder for project
|
|
40
49
|
|
|
41
50
|
}
|
|
@@ -135,7 +144,40 @@ function CreateSdkGen(opts: FullCreateSdkGenOptions) {
|
|
|
135
144
|
note: JSON.stringify(jopts, null, 2)
|
|
136
145
|
})
|
|
137
146
|
|
|
138
|
-
const
|
|
147
|
+
const kept = new Set<string>((rootModule.KEPT ?? [])
|
|
148
|
+
.map((rel: string) => SDK_FOLDER + '/' + rel))
|
|
149
|
+
|
|
150
|
+
const manage = (rel: string) => !kept.has(rel) &&
|
|
151
|
+
!rel.startsWith(SDK_FOLDER + '/def/') && !rel.startsWith(SDK_FOLDER + '/log/') &&
|
|
152
|
+
!rel.split('/').includes('.jostraca') && !rel.endsWith('.aon')
|
|
153
|
+
|
|
154
|
+
const record = readScaffold(fs, folder)
|
|
155
|
+
|
|
156
|
+
const jres = await withCopyMerge({
|
|
157
|
+
fs,
|
|
158
|
+
folder,
|
|
159
|
+
log,
|
|
160
|
+
mode: scaffoldMode(fs, folder),
|
|
161
|
+
what: 'create-sdkgen ' + name,
|
|
162
|
+
force: !!spec.force,
|
|
163
|
+
keep: !!spec.keep,
|
|
164
|
+
dryrun,
|
|
165
|
+
manage,
|
|
166
|
+
recorded: (rel: string) => record[rel],
|
|
167
|
+
fingerprint,
|
|
168
|
+
basedir: SCAFFOLD_BASE,
|
|
169
|
+
conflictdir: SCAFFOLD_CONFLICT,
|
|
170
|
+
versions: gitVersions,
|
|
171
|
+
}, async (gfs: any) => {
|
|
172
|
+
const res = await jostraca.generate({ ...jopts, fs: () => gfs },
|
|
173
|
+
() => CreateRoot({ model, spec }))
|
|
174
|
+
|
|
175
|
+
if (!dryrun) {
|
|
176
|
+
recordScaffold(gfs, folder, res, Pkg.version, manage, record)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return res
|
|
180
|
+
})
|
|
139
181
|
|
|
140
182
|
showChanges(jopts.log, 'generate-result', jres, process.cwd())
|
|
141
183
|
|
|
@@ -88,6 +88,12 @@ const PROJECT_STUB = `# Project overlay — YOURS. The scaffold creates this fil
|
|
|
88
88
|
# A published package name that does not follow the derivation:
|
|
89
89
|
#
|
|
90
90
|
# main: kit: target: ts: publish: registry: package: '@scope/name'
|
|
91
|
+
#
|
|
92
|
+
# How an add, or a re-scaffold, treats a file in .sdk the project changed:
|
|
93
|
+
# merged with the update (the default), or overwritten, for a project
|
|
94
|
+
# regenerated in full from the toolchain:
|
|
95
|
+
#
|
|
96
|
+
# main: kit: copies: 'overwrite'
|
|
91
97
|
`
|
|
92
98
|
|
|
93
99
|
|
|
@@ -303,5 +309,6 @@ const CreateRoot = cmp(function CreateRoot(props: any) {
|
|
|
303
309
|
|
|
304
310
|
export {
|
|
305
311
|
CreateRoot,
|
|
312
|
+
KEPT,
|
|
306
313
|
sanitizeDefName,
|
|
307
314
|
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/* Copyright (c) 2026 Richard Rodger, MIT License */
|
|
2
|
+
|
|
3
|
+
import Path from 'node:path'
|
|
4
|
+
import { createHash } from 'node:crypto'
|
|
5
|
+
|
|
6
|
+
import { copyMode } from './copymerge'
|
|
7
|
+
import type { CopyMode } from './copymerge'
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
// Relative to the project folder.
|
|
11
|
+
const SCAFFOLD_LOG = '.sdk/log/scaffold.jsonl'
|
|
12
|
+
const SCAFFOLD_BASE = '.sdk/log/scaffold-base'
|
|
13
|
+
const SCAFFOLD_CONFLICT = '.sdk/log/scaffold-conflict'
|
|
14
|
+
const PROJECT_MODEL = '.sdk/model/project.aontu'
|
|
15
|
+
|
|
16
|
+
const COPIES_RE = /^\s*main\s*:\s*kit\s*:\s*copies\s*:\s*['"]?(merge|overwrite)\b/m
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
// As @voxgig/sdkgen fingerprints its copies: CRLF and LF read the same.
|
|
20
|
+
function fingerprint(content: any): string {
|
|
21
|
+
const bytes = Buffer.isBuffer(content) ? content : Buffer.from(String(content))
|
|
22
|
+
const lf = Buffer.from(bytes.toString('latin1').replace(/\r\n/g, '\n'), 'latin1')
|
|
23
|
+
return createHash('sha256').update(lf).digest('hex').slice(0, 16)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
function at(folder: string, rel: string): string {
|
|
28
|
+
return Path.join(folder, ...rel.split('/'))
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
// `main.kit.copies`, read from the project overlay without building the model.
|
|
33
|
+
function scaffoldMode(fs: any, folder: string): CopyMode {
|
|
34
|
+
const path = at(folder, PROJECT_MODEL)
|
|
35
|
+
const text = fs.existsSync(path) ? String(fs.readFileSync(path, 'utf8')) : ''
|
|
36
|
+
return copyMode(COPIES_RE.exec(text)?.[1])
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
// Replays the append-only log: each line holds what one scaffold changed.
|
|
41
|
+
function readScaffold(fs: any, folder: string): Record<string, string> {
|
|
42
|
+
const files: Record<string, string> = {}
|
|
43
|
+
const path = at(folder, SCAFFOLD_LOG)
|
|
44
|
+
|
|
45
|
+
if (!fs.existsSync(path)) {
|
|
46
|
+
return files
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
for (const line of String(fs.readFileSync(path, 'utf8')).split('\n')) {
|
|
50
|
+
let entry: any
|
|
51
|
+
try {
|
|
52
|
+
entry = '' === line.trim() ? undefined : JSON.parse(line)
|
|
53
|
+
}
|
|
54
|
+
catch (err: any) {
|
|
55
|
+
continue
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
for (const [rel, print] of Object.entries(entry?.files ?? {})) {
|
|
59
|
+
if ('string' === typeof print) {
|
|
60
|
+
files[rel] = print
|
|
61
|
+
}
|
|
62
|
+
else if (null === print) {
|
|
63
|
+
delete files[rel]
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return files
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
function recordScaffold(
|
|
73
|
+
fs: any, folder: string, jres: any, version: string,
|
|
74
|
+
manage: (rel: string) => boolean, record: Record<string, string>,
|
|
75
|
+
) {
|
|
76
|
+
const written: string[] = [
|
|
77
|
+
...(jres?.files?.written ?? []), ...(jres?.files?.unchanged ?? []),
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
const files: Record<string, string> = {}
|
|
81
|
+
|
|
82
|
+
for (const path of written) {
|
|
83
|
+
const rel = Path.relative(Path.resolve(folder), Path.resolve(path))
|
|
84
|
+
.split(Path.sep).join('/')
|
|
85
|
+
|
|
86
|
+
if ('' === rel || rel.startsWith('../') || !manage(rel) || !fs.existsSync(path)) {
|
|
87
|
+
continue
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const print = fingerprint(fs.readFileSync(path))
|
|
91
|
+
if (print !== record[rel]) {
|
|
92
|
+
files[rel] = print
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (0 === Object.keys(files).length) {
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const sorted = Object.fromEntries(Object.keys(files).sort()
|
|
101
|
+
.map((rel: string) => [rel, files[rel]]))
|
|
102
|
+
|
|
103
|
+
const log = at(folder, SCAFFOLD_LOG)
|
|
104
|
+
fs.mkdirSync(Path.dirname(log), { recursive: true })
|
|
105
|
+
fs.appendFileSync(log, JSON.stringify({
|
|
106
|
+
at: new Date().toISOString(), op: 'scaffold', version, files: sorted,
|
|
107
|
+
}) + '\n')
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
export {
|
|
112
|
+
SCAFFOLD_LOG,
|
|
113
|
+
SCAFFOLD_BASE,
|
|
114
|
+
SCAFFOLD_CONFLICT,
|
|
115
|
+
fingerprint,
|
|
116
|
+
scaffoldMode,
|
|
117
|
+
readScaffold,
|
|
118
|
+
recordScaffold,
|
|
119
|
+
}
|