@timeax/scaffold 0.1.1 → 0.1.2
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/ast.d.cts +1 -1
- package/dist/ast.d.ts +1 -1
- package/dist/cli.cjs +65 -51
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.mjs +63 -50
- package/dist/cli.mjs.map +1 -1
- package/dist/{config-C0067l3c.d.cts → config-DBARTF0g.d.cts} +7 -0
- package/dist/{config-C0067l3c.d.ts → config-DBARTF0g.d.ts} +7 -0
- package/dist/index.cjs +52 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +50 -37
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -3
- package/src/core/apply-structure.ts +14 -0
- package/src/schema/hooks.ts +8 -0
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as ScaffoldConfig, a as ScanStructureOptions, b as ScanFromConfigOptions, c as StructureEntry } from './config-
|
|
2
|
-
export { B as BaseEntryOptions, D as DirEntry, F as FileEntry, l as FormatConfig, k as FormatMode, H as HookContext, e as HookFilter, h as RegularHookConfig, f as RegularHookFn, R as RegularHookKind, m as StructureGroupConfig, j as StubConfig, i as StubHookConfig, g as StubHookFn, d as StubHookKind } from './config-
|
|
1
|
+
import { S as ScaffoldConfig, a as ScanStructureOptions, b as ScanFromConfigOptions, c as StructureEntry } from './config-DBARTF0g.cjs';
|
|
2
|
+
export { B as BaseEntryOptions, D as DirEntry, F as FileEntry, l as FormatConfig, k as FormatMode, H as HookContext, e as HookFilter, h as RegularHookConfig, f as RegularHookFn, R as RegularHookKind, m as StructureGroupConfig, j as StubConfig, i as StubHookConfig, g as StubHookFn, d as StubHookKind } from './config-DBARTF0g.cjs';
|
|
3
3
|
|
|
4
4
|
declare const SCAFFOLD_ROOT_DIR = ".scaffold";
|
|
5
5
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as ScaffoldConfig, a as ScanStructureOptions, b as ScanFromConfigOptions, c as StructureEntry } from './config-
|
|
2
|
-
export { B as BaseEntryOptions, D as DirEntry, F as FileEntry, l as FormatConfig, k as FormatMode, H as HookContext, e as HookFilter, h as RegularHookConfig, f as RegularHookFn, R as RegularHookKind, m as StructureGroupConfig, j as StubConfig, i as StubHookConfig, g as StubHookFn, d as StubHookKind } from './config-
|
|
1
|
+
import { S as ScaffoldConfig, a as ScanStructureOptions, b as ScanFromConfigOptions, c as StructureEntry } from './config-DBARTF0g.js';
|
|
2
|
+
export { B as BaseEntryOptions, D as DirEntry, F as FileEntry, l as FormatConfig, k as FormatMode, H as HookContext, e as HookFilter, h as RegularHookConfig, f as RegularHookFn, R as RegularHookKind, m as StructureGroupConfig, j as StubConfig, i as StubHookConfig, g as StubHookFn, d as StubHookKind } from './config-DBARTF0g.js';
|
|
3
3
|
|
|
4
4
|
declare const SCAFFOLD_ROOT_DIR = ".scaffold";
|
|
5
5
|
|
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import
|
|
1
|
+
import path5 from 'path';
|
|
2
2
|
import fs2 from 'fs';
|
|
3
3
|
import os from 'os';
|
|
4
4
|
import crypto from 'crypto';
|
|
5
5
|
import { pathToFileURL } from 'url';
|
|
6
6
|
import { transform } from 'esbuild';
|
|
7
7
|
import { minimatch } from 'minimatch';
|
|
8
|
+
import pluralize from 'pluralize';
|
|
8
9
|
|
|
9
10
|
// src/schema/index.ts
|
|
10
11
|
var SCAFFOLD_ROOT_DIR = ".scaffold";
|
|
@@ -116,31 +117,31 @@ function statSafeSync(targetPath) {
|
|
|
116
117
|
}
|
|
117
118
|
}
|
|
118
119
|
function toProjectRelativePath(projectRoot, absolutePath) {
|
|
119
|
-
const absRoot =
|
|
120
|
-
const absTarget =
|
|
121
|
-
const rootWithSep = absRoot.endsWith(
|
|
120
|
+
const absRoot = path5.resolve(projectRoot);
|
|
121
|
+
const absTarget = path5.resolve(absolutePath);
|
|
122
|
+
const rootWithSep = absRoot.endsWith(path5.sep) ? absRoot : absRoot + path5.sep;
|
|
122
123
|
if (!absTarget.startsWith(rootWithSep) && absTarget !== absRoot) {
|
|
123
124
|
throw new Error(
|
|
124
125
|
`Path "${absTarget}" is not inside project root "${absRoot}".`
|
|
125
126
|
);
|
|
126
127
|
}
|
|
127
|
-
const rel =
|
|
128
|
+
const rel = path5.relative(absRoot, absTarget);
|
|
128
129
|
return toPosixPath(rel);
|
|
129
130
|
}
|
|
130
131
|
|
|
131
132
|
// src/core/config-loader.ts
|
|
132
133
|
var logger = defaultLogger.child("[config]");
|
|
133
134
|
async function loadScaffoldConfig(cwd, options = {}) {
|
|
134
|
-
const absCwd =
|
|
135
|
-
const initialScaffoldDir = options.scaffoldDir ?
|
|
135
|
+
const absCwd = path5.resolve(cwd);
|
|
136
|
+
const initialScaffoldDir = options.scaffoldDir ? path5.resolve(absCwd, options.scaffoldDir) : path5.join(absCwd, SCAFFOLD_ROOT_DIR);
|
|
136
137
|
const configPath = options.configPath ?? resolveConfigPath(initialScaffoldDir);
|
|
137
138
|
const config = await importConfig(configPath);
|
|
138
139
|
let configRoot = absCwd;
|
|
139
140
|
if (config.root) {
|
|
140
|
-
configRoot =
|
|
141
|
+
configRoot = path5.resolve(absCwd, config.root);
|
|
141
142
|
}
|
|
142
|
-
const scaffoldDir = options.scaffoldDir ?
|
|
143
|
-
const baseRoot = config.base ?
|
|
143
|
+
const scaffoldDir = options.scaffoldDir ? path5.resolve(absCwd, options.scaffoldDir) : path5.join(configRoot, SCAFFOLD_ROOT_DIR);
|
|
144
|
+
const baseRoot = config.base ? path5.resolve(configRoot, config.base) : configRoot;
|
|
144
145
|
logger.debug(
|
|
145
146
|
`Loaded config: configRoot=${configRoot}, baseRoot=${baseRoot}, scaffoldDir=${scaffoldDir}`
|
|
146
147
|
);
|
|
@@ -159,7 +160,7 @@ function resolveConfigPath(scaffoldDir) {
|
|
|
159
160
|
"config.cjs"
|
|
160
161
|
];
|
|
161
162
|
for (const file of candidates) {
|
|
162
|
-
const full =
|
|
163
|
+
const full = path5.join(scaffoldDir, file);
|
|
163
164
|
if (fs2.existsSync(full)) {
|
|
164
165
|
return full;
|
|
165
166
|
}
|
|
@@ -171,7 +172,7 @@ function resolveConfigPath(scaffoldDir) {
|
|
|
171
172
|
);
|
|
172
173
|
}
|
|
173
174
|
async function importConfig(configPath) {
|
|
174
|
-
const ext =
|
|
175
|
+
const ext = path5.extname(configPath).toLowerCase();
|
|
175
176
|
if (ext === ".ts" || ext === ".tsx") {
|
|
176
177
|
return importTsConfig(configPath);
|
|
177
178
|
}
|
|
@@ -183,9 +184,9 @@ async function importTsConfig(configPath) {
|
|
|
183
184
|
const source = fs2.readFileSync(configPath, "utf8");
|
|
184
185
|
const stat = fs2.statSync(configPath);
|
|
185
186
|
const hash = crypto.createHash("sha1").update(configPath).update(String(stat.mtimeMs)).digest("hex");
|
|
186
|
-
const tmpDir =
|
|
187
|
+
const tmpDir = path5.join(os.tmpdir(), "timeax-scaffold-config");
|
|
187
188
|
ensureDirSync(tmpDir);
|
|
188
|
-
const tmpFile =
|
|
189
|
+
const tmpFile = path5.join(tmpDir, `${hash}.mjs`);
|
|
189
190
|
if (!fs2.existsSync(tmpFile)) {
|
|
190
191
|
const result = await transform(source, {
|
|
191
192
|
loader: "ts",
|
|
@@ -779,7 +780,7 @@ function resolveGroupStructure(scaffoldDir, group, config) {
|
|
|
779
780
|
return group.structure;
|
|
780
781
|
}
|
|
781
782
|
const fileName = group.structureFile ?? `${group.name}.txt`;
|
|
782
|
-
const filePath =
|
|
783
|
+
const filePath = path5.join(scaffoldDir, fileName);
|
|
783
784
|
if (!fs2.existsSync(filePath)) {
|
|
784
785
|
throw new Error(
|
|
785
786
|
`@timeax/scaffold: Group "${group.name}" has no structure. Expected file "${fileName}" in "${scaffoldDir}".`
|
|
@@ -795,7 +796,7 @@ function resolveSingleStructure(scaffoldDir, config) {
|
|
|
795
796
|
return config.structure;
|
|
796
797
|
}
|
|
797
798
|
const fileName = config.structureFile ?? "structure.txt";
|
|
798
|
-
const filePath =
|
|
799
|
+
const filePath = path5.join(scaffoldDir, fileName);
|
|
799
800
|
if (!fs2.existsSync(filePath)) {
|
|
800
801
|
throw new Error(
|
|
801
802
|
`@timeax/scaffold: No structure defined. Expected "${fileName}" in "${scaffoldDir}".`
|
|
@@ -817,7 +818,7 @@ var CacheManager = class {
|
|
|
817
818
|
}
|
|
818
819
|
cache = DEFAULT_CACHE;
|
|
819
820
|
get cachePathAbs() {
|
|
820
|
-
return
|
|
821
|
+
return path5.resolve(this.projectRoot, this.cacheFileRelPath);
|
|
821
822
|
}
|
|
822
823
|
load() {
|
|
823
824
|
const cachePath = this.cachePathAbs;
|
|
@@ -841,7 +842,7 @@ var CacheManager = class {
|
|
|
841
842
|
}
|
|
842
843
|
save() {
|
|
843
844
|
const cachePath = this.cachePathAbs;
|
|
844
|
-
const dir =
|
|
845
|
+
const dir = path5.dirname(cachePath);
|
|
845
846
|
ensureDirSync(dir);
|
|
846
847
|
fs2.writeFileSync(cachePath, JSON.stringify(this.cache, null, 2), "utf8");
|
|
847
848
|
}
|
|
@@ -926,9 +927,9 @@ async function applyStructure(opts) {
|
|
|
926
927
|
interactiveDelete
|
|
927
928
|
} = opts;
|
|
928
929
|
const logger5 = opts.logger ?? defaultLogger.child(groupName ? `[apply:${groupName}]` : "[apply]");
|
|
929
|
-
const projectRootAbs =
|
|
930
|
-
const baseDirAbs =
|
|
931
|
-
baseDirAbs.endsWith(
|
|
930
|
+
const projectRootAbs = path5.resolve(projectRoot);
|
|
931
|
+
const baseDirAbs = path5.resolve(baseDir);
|
|
932
|
+
baseDirAbs.endsWith(path5.sep) ? baseDirAbs : baseDirAbs + path5.sep;
|
|
932
933
|
const desiredPaths = /* @__PURE__ */ new Set();
|
|
933
934
|
const threshold = sizePromptThreshold ?? config.sizePromptThreshold ?? 128 * 1024;
|
|
934
935
|
async function walk(entry, inheritedStub) {
|
|
@@ -941,7 +942,7 @@ async function applyStructure(opts) {
|
|
|
941
942
|
}
|
|
942
943
|
async function handleDir(entry, inheritedStub) {
|
|
943
944
|
const relFromBase = entry.path.replace(/^[./]+/, "");
|
|
944
|
-
const absDir =
|
|
945
|
+
const absDir = path5.resolve(baseDirAbs, relFromBase);
|
|
945
946
|
const relFromRoot = toPosixPath(
|
|
946
947
|
toProjectRelativePath(projectRootAbs, absDir)
|
|
947
948
|
);
|
|
@@ -956,24 +957,30 @@ async function applyStructure(opts) {
|
|
|
956
957
|
}
|
|
957
958
|
async function handleFile(entry, inheritedStub) {
|
|
958
959
|
const relFromBase = entry.path.replace(/^[./]+/, "");
|
|
959
|
-
const absFile =
|
|
960
|
+
const absFile = path5.resolve(baseDirAbs, relFromBase);
|
|
960
961
|
const relFromRoot = toPosixPath(
|
|
961
962
|
toProjectRelativePath(projectRootAbs, absFile)
|
|
962
963
|
);
|
|
963
964
|
desiredPaths.add(relFromRoot);
|
|
964
965
|
const stubName = entry.stub ?? inheritedStub;
|
|
966
|
+
const extension = path5.extname(relFromRoot);
|
|
967
|
+
const fileName = path5.basename(relFromRoot, extension);
|
|
965
968
|
const ctx = {
|
|
966
969
|
projectRoot: projectRootAbs,
|
|
967
970
|
targetPath: relFromRoot,
|
|
968
971
|
absolutePath: absFile,
|
|
969
972
|
isDirectory: false,
|
|
973
|
+
fileName,
|
|
974
|
+
dirName: path5.dirname(relFromRoot),
|
|
975
|
+
extension,
|
|
976
|
+
pluralFileName: pluralize.plural(fileName),
|
|
970
977
|
stubName
|
|
971
978
|
};
|
|
972
979
|
if (fs2.existsSync(absFile)) {
|
|
973
980
|
return;
|
|
974
981
|
}
|
|
975
982
|
await hooks.runRegular("preCreateFile", ctx);
|
|
976
|
-
const dir =
|
|
983
|
+
const dir = path5.dirname(absFile);
|
|
977
984
|
ensureDirSync(dir);
|
|
978
985
|
if (stubName) {
|
|
979
986
|
await hooks.runStub("preStub", ctx);
|
|
@@ -1016,7 +1023,7 @@ async function applyStructure(opts) {
|
|
|
1016
1023
|
if (desiredPaths.has(cachedPath)) {
|
|
1017
1024
|
continue;
|
|
1018
1025
|
}
|
|
1019
|
-
const abs =
|
|
1026
|
+
const abs = path5.resolve(projectRoot, cachedPath);
|
|
1020
1027
|
const stats = statSafeSync(abs);
|
|
1021
1028
|
if (!stats) {
|
|
1022
1029
|
cache.delete(cachedPath);
|
|
@@ -1026,11 +1033,17 @@ async function applyStructure(opts) {
|
|
|
1026
1033
|
cache.delete(cachedPath);
|
|
1027
1034
|
continue;
|
|
1028
1035
|
}
|
|
1036
|
+
const extension = path5.extname(abs);
|
|
1037
|
+
const fileName = path5.basename(abs, extension);
|
|
1029
1038
|
const ctx = {
|
|
1030
1039
|
projectRoot,
|
|
1031
1040
|
targetPath: cachedPath,
|
|
1032
1041
|
absolutePath: abs,
|
|
1033
1042
|
isDirectory: false,
|
|
1043
|
+
fileName,
|
|
1044
|
+
dirName: path5.dirname(cachedPath),
|
|
1045
|
+
extension,
|
|
1046
|
+
pluralFileName: pluralize.plural(fileName),
|
|
1034
1047
|
stubName: entry?.createdByStub
|
|
1035
1048
|
};
|
|
1036
1049
|
await hooks.runRegular("preDeleteFile", ctx);
|
|
@@ -1062,16 +1075,16 @@ async function applyStructure(opts) {
|
|
|
1062
1075
|
}
|
|
1063
1076
|
}
|
|
1064
1077
|
function getStructureFilesFromConfig(projectRoot, scaffoldDir, config) {
|
|
1065
|
-
const baseDir =
|
|
1078
|
+
const baseDir = path5.resolve(projectRoot, scaffoldDir || SCAFFOLD_ROOT_DIR);
|
|
1066
1079
|
const files = [];
|
|
1067
1080
|
if (config.groups && config.groups.length > 0) {
|
|
1068
1081
|
for (const group of config.groups) {
|
|
1069
1082
|
const structureFile = group.structureFile && group.structureFile.trim().length ? group.structureFile : `${group.name}.txt`;
|
|
1070
|
-
files.push(
|
|
1083
|
+
files.push(path5.join(baseDir, structureFile));
|
|
1071
1084
|
}
|
|
1072
1085
|
} else {
|
|
1073
1086
|
const structureFile = config.structureFile || "structure.txt";
|
|
1074
|
-
files.push(
|
|
1087
|
+
files.push(path5.join(baseDir, structureFile));
|
|
1075
1088
|
}
|
|
1076
1089
|
return files;
|
|
1077
1090
|
}
|
|
@@ -1113,7 +1126,7 @@ async function runOnce(cwd, options = {}) {
|
|
|
1113
1126
|
const hooks = new HookRunner(config);
|
|
1114
1127
|
if (config.groups && config.groups.length > 0) {
|
|
1115
1128
|
for (const group of config.groups) {
|
|
1116
|
-
const groupRootAbs =
|
|
1129
|
+
const groupRootAbs = path5.resolve(projectRoot, group.root);
|
|
1117
1130
|
const structure = resolveGroupStructure(scaffoldDir, group, config);
|
|
1118
1131
|
const groupLogger = logger5.child(`[group:${group.name}]`);
|
|
1119
1132
|
await applyStructure({
|
|
@@ -1158,12 +1171,12 @@ var DEFAULT_IGNORE = [
|
|
|
1158
1171
|
"coverage/**"
|
|
1159
1172
|
];
|
|
1160
1173
|
function scanDirectoryToStructureText(rootDir, options = {}) {
|
|
1161
|
-
const absRoot =
|
|
1174
|
+
const absRoot = path5.resolve(rootDir);
|
|
1162
1175
|
const lines = [];
|
|
1163
1176
|
const ignorePatterns = options.ignore ?? DEFAULT_IGNORE;
|
|
1164
1177
|
const maxDepth = options.maxDepth ?? Infinity;
|
|
1165
1178
|
function isIgnored(absPath) {
|
|
1166
|
-
const rel = toPosixPath(
|
|
1179
|
+
const rel = toPosixPath(path5.relative(absRoot, absPath));
|
|
1167
1180
|
if (!rel || rel === ".") return false;
|
|
1168
1181
|
return ignorePatterns.some(
|
|
1169
1182
|
(pattern) => minimatch(rel, pattern, { dot: true })
|
|
@@ -1184,7 +1197,7 @@ function scanDirectoryToStructureText(rootDir, options = {}) {
|
|
|
1184
1197
|
});
|
|
1185
1198
|
for (const dirent of dirents) {
|
|
1186
1199
|
const name = dirent.name;
|
|
1187
|
-
const absPath =
|
|
1200
|
+
const absPath = path5.join(currentAbs, name);
|
|
1188
1201
|
if (isIgnored(absPath)) continue;
|
|
1189
1202
|
const indent = " ".repeat(depth);
|
|
1190
1203
|
if (dirent.isDirectory()) {
|
|
@@ -1207,13 +1220,13 @@ async function scanProjectFromConfig(cwd, options = {}) {
|
|
|
1207
1220
|
const onlyGroups = options.groups;
|
|
1208
1221
|
const results = [];
|
|
1209
1222
|
function scanGroup(cfg, group) {
|
|
1210
|
-
const rootAbs =
|
|
1223
|
+
const rootAbs = path5.resolve(projectRoot, group.root);
|
|
1211
1224
|
const text = scanDirectoryToStructureText(rootAbs, {
|
|
1212
1225
|
ignore: ignorePatterns,
|
|
1213
1226
|
maxDepth
|
|
1214
1227
|
});
|
|
1215
1228
|
const structureFileName = group.structureFile ?? `${group.name}.txt`;
|
|
1216
|
-
const structureFilePath =
|
|
1229
|
+
const structureFilePath = path5.join(scaffoldDir, structureFileName);
|
|
1217
1230
|
return {
|
|
1218
1231
|
groupName: group.name,
|
|
1219
1232
|
groupRoot: group.root,
|
|
@@ -1240,7 +1253,7 @@ async function scanProjectFromConfig(cwd, options = {}) {
|
|
|
1240
1253
|
maxDepth
|
|
1241
1254
|
});
|
|
1242
1255
|
const structureFileName = config.structureFile ?? "structure.txt";
|
|
1243
|
-
const structureFilePath =
|
|
1256
|
+
const structureFilePath = path5.join(scaffoldDir, structureFileName);
|
|
1244
1257
|
results.push({
|
|
1245
1258
|
groupName: "default",
|
|
1246
1259
|
groupRoot: ".",
|
|
@@ -1274,8 +1287,8 @@ async function ensureStructureFilesFromConfig(cwd, options = {}) {
|
|
|
1274
1287
|
const seen = /* @__PURE__ */ new Set();
|
|
1275
1288
|
const ensureFile = (fileName) => {
|
|
1276
1289
|
if (!fileName) return;
|
|
1277
|
-
const filePath =
|
|
1278
|
-
const key =
|
|
1290
|
+
const filePath = path5.join(scaffoldDir, fileName);
|
|
1291
|
+
const key = path5.resolve(filePath);
|
|
1279
1292
|
if (seen.has(key)) return;
|
|
1280
1293
|
seen.add(key);
|
|
1281
1294
|
if (fs2.existsSync(filePath)) {
|