mikser-io 10.10.1 → 10.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/package.json +1 -1
- package/src/engine.js +17 -5
- package/src/manifest.js +47 -19
- package/src/plugins/assets.js +23 -0
package/package.json
CHANGED
package/src/engine.js
CHANGED
|
@@ -835,7 +835,17 @@ The full version, with what each code means: docs/diagnostics.md`)
|
|
|
835
835
|
// creates for itself. By `import` every onLoaded has run and the
|
|
836
836
|
// registry is complete. Nothing is imported, because this exits first,
|
|
837
837
|
// the same way --explain and --audit-output do.
|
|
838
|
-
|
|
838
|
+
//
|
|
839
|
+
// --audit-output is here for the same reason, found the same way. It
|
|
840
|
+
// asks which trees hold outputs, and a plugin cannot answer until its
|
|
841
|
+
// own onLoaded has run — the assets plugin resolves the folder its
|
|
842
|
+
// derivatives live in there, because `--assets` is a plugin option
|
|
843
|
+
// and stage two of the parse is what makes it readable. Dispatched
|
|
844
|
+
// from the engine's onLoaded it ran ahead of all of them, so the one
|
|
845
|
+
// check that reports unclaimed files could not see the one tree whose
|
|
846
|
+
// files reach the site through a symlink it does not follow. It
|
|
847
|
+
// reported `0 orphaned` over any amount of debris.
|
|
848
|
+
if (runtime.options.tools || runtime.options.tool || runtime.options.auditOutput) {
|
|
839
849
|
const code = await runReportOnly()
|
|
840
850
|
if (code !== null) process.exit(code)
|
|
841
851
|
}
|
|
@@ -877,14 +887,16 @@ The full version, with what each code means: docs/diagnostics.md`)
|
|
|
877
887
|
// one implementation, so a forwarded --audit-output cannot disagree with a
|
|
878
888
|
// local one about what it checked.
|
|
879
889
|
//
|
|
880
|
-
// --tools and --
|
|
881
|
-
// instead, because
|
|
882
|
-
// plugin's onLoaded has run and this hook runs
|
|
890
|
+
// --tools, --tool and --audit-output are NOT among them: they are
|
|
891
|
+
// dispatched at `import` instead, because what they read is not
|
|
892
|
+
// complete until every plugin's onLoaded has run and this hook runs
|
|
893
|
+
// ahead of all of them — the tool registry for the first two, the set
|
|
894
|
+
// of trees that hold outputs for the third.
|
|
883
895
|
// The import dispatch existed already and was documented as
|
|
884
896
|
// load-bearing, but this call reached the same code first and exited,
|
|
885
897
|
// so it never ran — and every tool a plugin registers was missing from
|
|
886
898
|
// the CLI while the listing looked healthy, just short.
|
|
887
|
-
if (!runtime.options.tools && !runtime.options.tool) {
|
|
899
|
+
if (!runtime.options.tools && !runtime.options.tool && !runtime.options.auditOutput) {
|
|
888
900
|
const code = await runReportOnly()
|
|
889
901
|
if (code !== null) process.exit(code)
|
|
890
902
|
}
|
package/src/manifest.js
CHANGED
|
@@ -1102,7 +1102,7 @@ export function createManifest(db) {
|
|
|
1102
1102
|
// Walk the output folder against recorded snapshots, returning
|
|
1103
1103
|
// a diff describing missing / mismatched / orphaned /
|
|
1104
1104
|
// unverifiable. Backs `mikser --audit-output`. Pure: no mutations.
|
|
1105
|
-
async auditOutput({ outputFolder } = {}) {
|
|
1105
|
+
async auditOutput({ outputFolder, roots } = {}) {
|
|
1106
1106
|
outputFolder = outputFolder || runtime.options.outputFolder
|
|
1107
1107
|
const missing = []
|
|
1108
1108
|
const mismatched = []
|
|
@@ -1112,16 +1112,12 @@ export function createManifest(db) {
|
|
|
1112
1112
|
const snap = rowToSnap(row)
|
|
1113
1113
|
if (!snap.destination) continue
|
|
1114
1114
|
const filePath = resolveOutputPath(snap.destination, outputFolder)
|
|
1115
|
-
//
|
|
1116
|
-
//
|
|
1117
|
-
//
|
|
1118
|
-
//
|
|
1119
|
-
//
|
|
1120
|
-
|
|
1121
|
-
const relative = path.relative(outputFolder, filePath)
|
|
1122
|
-
if (relative && !relative.startsWith('..') && !path.isAbsolute(relative)) {
|
|
1123
|
-
claimed.add(relative)
|
|
1124
|
-
}
|
|
1115
|
+
// Claimed by ABSOLUTE path, so the set does not depend on the
|
|
1116
|
+
// relative form any particular walk produces. It was keyed on
|
|
1117
|
+
// a path relative to outputFolder, which worked only while
|
|
1118
|
+
// that was the one tree walked — and quietly claimed nothing
|
|
1119
|
+
// for every destination outside it.
|
|
1120
|
+
claimed.add(path.resolve(filePath))
|
|
1125
1121
|
if (!existsSync(filePath)) {
|
|
1126
1122
|
missing.push({ id: snap.id, destination: snap.destination })
|
|
1127
1123
|
continue
|
|
@@ -1150,15 +1146,47 @@ export function createManifest(db) {
|
|
|
1150
1146
|
}
|
|
1151
1147
|
}
|
|
1152
1148
|
const { globby } = await import('globby')
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1149
|
+
// Every tree mikser writes into, not only the output folder.
|
|
1150
|
+
//
|
|
1151
|
+
// Preset derivatives live at the working-folder root and reach
|
|
1152
|
+
// the site through a symlink, which the walk does not follow — so
|
|
1153
|
+
// a file nothing claims there was invisible to this check, which
|
|
1154
|
+
// is the one place the concept of an orphan exists. Plugins
|
|
1155
|
+
// declare their own trees on `runtime.options.auditRoots`,
|
|
1156
|
+
// carrying their own ignore patterns, so nothing here has to know
|
|
1157
|
+
// what a preset is or that a `.md5` beside a derivative is
|
|
1158
|
+
// bookkeeping rather than output.
|
|
1159
|
+
const declared = [{ path: outputFolder }, ...(roots ?? runtime.options.auditRoots ?? [])]
|
|
1160
|
+
const walked = []
|
|
1161
|
+
for (const root of declared) {
|
|
1162
|
+
const resolved = path.resolve(root.path ?? root)
|
|
1163
|
+
// A root inside one already walked would report every file in
|
|
1164
|
+
// it twice.
|
|
1165
|
+
const nested = walked.some(({ resolved: seen }) =>
|
|
1166
|
+
resolved === seen || resolved.startsWith(seen + path.sep))
|
|
1167
|
+
if (nested) continue
|
|
1168
|
+
walked.push({ resolved, ignore: root.ignore ?? [] })
|
|
1169
|
+
}
|
|
1158
1170
|
const orphaned = []
|
|
1159
|
-
for (const
|
|
1160
|
-
|
|
1161
|
-
|
|
1171
|
+
for (const { resolved, ignore } of walked) {
|
|
1172
|
+
const onDisk = await globby('**/*', {
|
|
1173
|
+
cwd: resolved,
|
|
1174
|
+
onlyFiles: true,
|
|
1175
|
+
followSymbolicLinks: false,
|
|
1176
|
+
ignore,
|
|
1177
|
+
})
|
|
1178
|
+
for (const rel of onDisk) {
|
|
1179
|
+
if (claimed.has(path.join(resolved, rel))) continue
|
|
1180
|
+
// Relative to the output folder for the output folder, as
|
|
1181
|
+
// before; relative to the working folder for anything
|
|
1182
|
+
// else, because `web/media/x.jpg` alone does not say which
|
|
1183
|
+
// tree it is in.
|
|
1184
|
+
const isOutput = resolved === path.resolve(outputFolder)
|
|
1185
|
+
const display = isOutput
|
|
1186
|
+
? rel
|
|
1187
|
+
: path.relative(runtime.options.workingFolder ?? resolved, path.join(resolved, rel))
|
|
1188
|
+
orphaned.push({ path: display, root: resolved })
|
|
1189
|
+
}
|
|
1162
1190
|
}
|
|
1163
1191
|
// Reported alongside, not as a mismatch: two entities claiming one
|
|
1164
1192
|
// destination usually produces NO mismatch at all, because each
|
package/src/plugins/assets.js
CHANGED
|
@@ -505,6 +505,7 @@ export function assets(options = {}) {
|
|
|
505
505
|
runtime.engine ??= {}
|
|
506
506
|
runtime.engine.assets = { explainMissing }
|
|
507
507
|
|
|
508
|
+
|
|
508
509
|
// `??`, not `||`: an option commander did not see is undefined, and
|
|
509
510
|
// falling through to the config is the point. `||` would also fall
|
|
510
511
|
// through for an intentional empty string, which is a different
|
|
@@ -516,6 +517,28 @@ export function assets(options = {}) {
|
|
|
516
517
|
|
|
517
518
|
runtime.options.assets = runtime.options.assets ?? options.assetsFolder ?? 'assets'
|
|
518
519
|
runtime.options.assetsFolder = path.join(runtime.options.workingFolder, runtime.options.assets)
|
|
520
|
+
// This tree holds outputs too, and --audit-output could not see it:
|
|
521
|
+
// it walks the output folder, and derivatives reach the site through
|
|
522
|
+
// a symlink the walk does not follow. Declared rather than hardcoded
|
|
523
|
+
// in the manifest, with the ignore that only this plugin can state —
|
|
524
|
+
// a `.md5` beside a derivative is bookkeeping, and without excluding
|
|
525
|
+
// them every derivative on the site would report one orphan.
|
|
526
|
+
//
|
|
527
|
+
// `auditIgnore` is the escape hatch for a preset that writes MORE
|
|
528
|
+
// than one file. The engine records the one destination it handed
|
|
529
|
+
// over, so a poster frame written beside a video is genuinely
|
|
530
|
+
// unclaimed and genuinely reported — correct, and a permanent
|
|
531
|
+
// non-zero exit for a legitimate preset if there were no way to say
|
|
532
|
+
// "these are expected". Stated in config, next to the preset that
|
|
533
|
+
// produces them, rather than inferred.
|
|
534
|
+
const auditRoot = {
|
|
535
|
+
path: runtime.options.assetsFolder,
|
|
536
|
+
ignore: ['**/*.md5', ...(options.auditIgnore ?? [])],
|
|
537
|
+
}
|
|
538
|
+
runtime.options.auditRoots = [
|
|
539
|
+
...(runtime.options.auditRoots ?? []).filter(root => root.path !== auditRoot.path),
|
|
540
|
+
auditRoot,
|
|
541
|
+
]
|
|
519
542
|
logger.debug('Assets folder: %s', runtime.options.assetsFolder)
|
|
520
543
|
|
|
521
544
|
// --clear means "throw away what was derived and build it again", and
|