@thegetty/quire-cli 1.0.0-rc.44 → 1.0.0-rc.46
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/CHANGELOG.md +0 -36
- package/README.md +0 -9
- package/bin/cli.js +1 -46
- package/package.json +9 -25
- package/src/Command.js +6 -26
- package/src/commands/README.md +122 -213
- package/src/commands/build.js +37 -45
- package/src/commands/clean.js +19 -33
- package/src/commands/conf.js +43 -0
- package/src/commands/create.js +14 -42
- package/src/commands/epub.js +31 -67
- package/src/commands/index.js +2 -9
- package/src/commands/info.js +86 -88
- package/src/commands/pdf.js +82 -63
- package/src/commands/preview.js +31 -32
- package/src/commands/validate.js +20 -64
- package/src/commands/version.js +43 -0
- package/src/errors/validation/validation-error.js +11 -20
- package/src/helpers/clean.js +1 -1
- package/src/helpers/is-quire.js +31 -0
- package/src/helpers/test-cwd.js +6 -5
- package/src/helpers/which.js +4 -10
- package/src/lib/11ty/README.md +19 -135
- package/src/lib/11ty/api.js +93 -235
- package/src/lib/11ty/cli.js +37 -112
- package/src/lib/11ty/index.js +5 -64
- package/src/lib/11ty/paths.js +103 -0
- package/src/lib/README.md +0 -330
- package/src/lib/conf/README.md +69 -86
- package/src/lib/conf/config.js +3 -5
- package/src/lib/conf/defaults.js +0 -65
- package/src/lib/conf/schema.js +9 -103
- package/src/lib/epub/README.md +2 -133
- package/src/lib/epub/epub.js +11 -36
- package/src/lib/epub/index.js +21 -111
- package/src/lib/epub/pandoc.js +4 -33
- package/src/lib/git/README.md +2 -151
- package/src/lib/git/index.js +13 -217
- package/src/lib/i18n/README.md +3 -0
- package/src/lib/i18n/config.js +53 -0
- package/src/lib/i18n/index.js +43 -0
- package/src/lib/i18n/localeService.js +58 -0
- package/src/lib/pdf/README.md +0 -131
- package/src/lib/pdf/index.js +21 -124
- package/src/lib/pdf/paged.js +52 -100
- package/src/lib/pdf/prince.js +37 -115
- package/src/lib/pdf/split.js +33 -61
- package/src/lib/quire/README.md +19 -0
- package/src/lib/quire/index.js +353 -0
- package/src/lib/reporter/README.md +2 -211
- package/src/lib/reporter/index.js +0 -607
- package/src/main.js +48 -221
- package/src/packageConfig.js +0 -17
- package/src/validators/utils.js +1 -2
- package/schemas/config.schema.json +0 -194
- package/schemas/figures.schema.json +0 -56
- package/schemas/layout.schema.json +0 -5
- package/schemas/objects.schema.json +0 -62
- package/schemas/publication.schema.json +0 -140
- package/schemas/references.schema.json +0 -29
- package/src/Command.spec.js +0 -99
- package/src/commands/build.spec.js +0 -113
- package/src/commands/build.test.js +0 -397
- package/src/commands/clean.spec.js +0 -118
- package/src/commands/clean.test.js +0 -382
- package/src/commands/config.js +0 -238
- package/src/commands/config.spec.js +0 -107
- package/src/commands/config.test.js +0 -845
- package/src/commands/create.spec.js +0 -112
- package/src/commands/create.test.js +0 -415
- package/src/commands/doctor.js +0 -251
- package/src/commands/doctor.spec.js +0 -115
- package/src/commands/doctor.test.js +0 -1409
- package/src/commands/epub.spec.js +0 -114
- package/src/commands/epub.test.js +0 -503
- package/src/commands/help.js +0 -60
- package/src/commands/help.test.js +0 -132
- package/src/commands/info.spec.js +0 -72
- package/src/commands/info.test.js +0 -512
- package/src/commands/pdf.spec.js +0 -114
- package/src/commands/pdf.test.js +0 -464
- package/src/commands/preview.spec.js +0 -97
- package/src/commands/preview.test.js +0 -250
- package/src/commands/use.js +0 -56
- package/src/commands/use.spec.js +0 -61
- package/src/commands/use.test.js +0 -280
- package/src/commands/validate.spec.js +0 -90
- package/src/commands/validate.test.js +0 -378
- package/src/commands/workflows.js +0 -70
- package/src/errors/build/build-failed-error.js +0 -19
- package/src/errors/build/config-field-missing-error.js +0 -20
- package/src/errors/build/config-file-not-found-error.js +0 -20
- package/src/errors/build/index.js +0 -11
- package/src/errors/help/help-topic-not-found-error.js +0 -30
- package/src/errors/help/index.js +0 -8
- package/src/errors/index.js +0 -58
- package/src/errors/input/index.js +0 -8
- package/src/errors/input/invalid-input-error.js +0 -21
- package/src/errors/install/dependency-install-error.js +0 -19
- package/src/errors/install/directory-not-empty-error.js +0 -25
- package/src/errors/install/index.js +0 -12
- package/src/errors/install/invalid-path-error.js +0 -31
- package/src/errors/install/invalid-starter-error.js +0 -27
- package/src/errors/install/version-not-found-error.js +0 -35
- package/src/errors/output/epub-generation-error.js +0 -19
- package/src/errors/output/index.js +0 -14
- package/src/errors/output/invalid-epub-library-error.js +0 -20
- package/src/errors/output/invalid-pdf-library-error.js +0 -20
- package/src/errors/output/missing-build-output-error.js +0 -21
- package/src/errors/output/pdf-generation-error.js +0 -24
- package/src/errors/output/tool-not-found-error.js +0 -37
- package/src/errors/project/index.js +0 -10
- package/src/errors/project/not-in-project-error.js +0 -20
- package/src/errors/project/project-create-error.js +0 -21
- package/src/errors/quire-error.js +0 -27
- package/src/errors/validation/index.js +0 -3
- package/src/errors/validation/unknown-config-key-error.js +0 -27
- package/src/errors/validation/unknown-config-operation-error.js +0 -24
- package/src/helpers/docs-url.js +0 -32
- package/src/helpers/pager.js +0 -59
- package/src/helpers/pager.test.js +0 -85
- package/src/helpers/suggest-similar.js +0 -124
- package/src/helpers/suggest-similar.test.js +0 -108
- package/src/helpers/test-cwd.test.js +0 -192
- package/src/lib/11ty/index.test.js +0 -655
- package/src/lib/commander/index.js +0 -103
- package/src/lib/commander/index.test.js +0 -86
- package/src/lib/commander/options.js +0 -237
- package/src/lib/commander/options.test.js +0 -205
- package/src/lib/conf/build-status.js +0 -103
- package/src/lib/conf/build-status.test.js +0 -247
- package/src/lib/conf/config.test.js +0 -281
- package/src/lib/conf/format.js +0 -60
- package/src/lib/conf/format.test.js +0 -106
- package/src/lib/conf/helpers.js +0 -91
- package/src/lib/conf/helpers.test.js +0 -136
- package/src/lib/conf/index.js +0 -22
- package/src/lib/constants.js +0 -28
- package/src/lib/doctor/README.md +0 -667
- package/src/lib/doctor/checks/environment/cli-version.js +0 -62
- package/src/lib/doctor/checks/environment/cli-version.test.js +0 -132
- package/src/lib/doctor/checks/environment/git-available.js +0 -77
- package/src/lib/doctor/checks/environment/git-available.test.js +0 -52
- package/src/lib/doctor/checks/environment/index.js +0 -13
- package/src/lib/doctor/checks/environment/node-version.js +0 -66
- package/src/lib/doctor/checks/environment/node-version.test.js +0 -17
- package/src/lib/doctor/checks/environment/npm-available.js +0 -66
- package/src/lib/doctor/checks/environment/npm-available.test.js +0 -52
- package/src/lib/doctor/checks/environment/os-info.js +0 -48
- package/src/lib/doctor/checks/environment/os-info.test.js +0 -81
- package/src/lib/doctor/checks/environment/runtime-info.js +0 -51
- package/src/lib/doctor/checks/environment/runtime-info.test.js +0 -132
- package/src/lib/doctor/checks/outputs/epub-output.js +0 -119
- package/src/lib/doctor/checks/outputs/epub-output.test.js +0 -277
- package/src/lib/doctor/checks/outputs/index.js +0 -10
- package/src/lib/doctor/checks/outputs/pdf-output.js +0 -144
- package/src/lib/doctor/checks/outputs/pdf-output.test.js +0 -377
- package/src/lib/doctor/checks/outputs/stale-build.js +0 -122
- package/src/lib/doctor/checks/outputs/stale-build.test.js +0 -282
- package/src/lib/doctor/checks/project/data-files.js +0 -56
- package/src/lib/doctor/checks/project/data-files.test.js +0 -125
- package/src/lib/doctor/checks/project/dependencies.js +0 -53
- package/src/lib/doctor/checks/project/dependencies.test.js +0 -71
- package/src/lib/doctor/checks/project/index.js +0 -11
- package/src/lib/doctor/checks/project/quire-11ty.js +0 -98
- package/src/lib/doctor/checks/project/quire-11ty.test.js +0 -170
- package/src/lib/doctor/checks/project/quire-project.js +0 -38
- package/src/lib/doctor/checks/project/quire-project.test.js +0 -47
- package/src/lib/doctor/checks/tools/index.js +0 -10
- package/src/lib/doctor/checks/tools/pandoc-available.js +0 -82
- package/src/lib/doctor/checks/tools/pandoc-available.test.js +0 -73
- package/src/lib/doctor/checks/tools/prince-available.js +0 -81
- package/src/lib/doctor/checks/tools/prince-available.test.js +0 -73
- package/src/lib/doctor/constants.js +0 -39
- package/src/lib/doctor/formatDuration.js +0 -108
- package/src/lib/doctor/formatDuration.test.js +0 -76
- package/src/lib/doctor/formatters/human.js +0 -257
- package/src/lib/doctor/formatters/human.test.js +0 -463
- package/src/lib/doctor/formatters/index.js +0 -8
- package/src/lib/doctor/formatters/json.js +0 -78
- package/src/lib/doctor/formatters/json.test.js +0 -174
- package/src/lib/doctor/formatters/shared.js +0 -129
- package/src/lib/doctor/formatters/shared.test.js +0 -194
- package/src/lib/doctor/index.js +0 -271
- package/src/lib/doctor/index.test.js +0 -797
- package/src/lib/epub/engines.js +0 -46
- package/src/lib/epub/index.test.js +0 -518
- package/src/lib/epub/pandoc.test.js +0 -122
- package/src/lib/epub/schema.js +0 -21
- package/src/lib/error/README.md +0 -170
- package/src/lib/error/handler.js +0 -107
- package/src/lib/git/index.spec.js +0 -80
- package/src/lib/git/index.test.js +0 -453
- package/src/lib/help/frontmatter.js +0 -77
- package/src/lib/help/frontmatter.test.js +0 -139
- package/src/lib/help/index.js +0 -146
- package/src/lib/help/index.test.js +0 -188
- package/src/lib/help/topics/configuration.md +0 -77
- package/src/lib/help/topics/debugging.md +0 -69
- package/src/lib/help/topics/epub.md +0 -74
- package/src/lib/help/topics/pdf.md +0 -74
- package/src/lib/help/topics/publishing.md +0 -80
- package/src/lib/help/topics/workflows.md +0 -50
- package/src/lib/installer/index.js +0 -309
- package/src/lib/installer/index.spec.js +0 -83
- package/src/lib/installer/index.test.js +0 -545
- package/src/lib/logger/README.md +0 -424
- package/src/lib/logger/debug.js +0 -132
- package/src/lib/logger/debug.spec.js +0 -130
- package/src/lib/logger/debug.test.js +0 -128
- package/src/lib/logger/index.js +0 -228
- package/src/lib/logger/index.spec.js +0 -131
- package/src/lib/logger/index.test.js +0 -578
- package/src/lib/npm/README.md +0 -127
- package/src/lib/npm/index.js +0 -198
- package/src/lib/npm/index.spec.js +0 -60
- package/src/lib/npm/index.test.js +0 -355
- package/src/lib/pdf/engines.js +0 -46
- package/src/lib/pdf/index.test.js +0 -708
- package/src/lib/pdf/paged.test.js +0 -366
- package/src/lib/pdf/prince.test.js +0 -202
- package/src/lib/pdf/schema.js +0 -21
- package/src/lib/pdf/split.test.js +0 -445
- package/src/lib/platform.js +0 -95
- package/src/lib/process/manager.js +0 -110
- package/src/lib/process/manager.test.js +0 -55
- package/src/lib/project/build.js +0 -162
- package/src/lib/project/build.test.js +0 -275
- package/src/lib/project/config.js +0 -48
- package/src/lib/project/config.test.js +0 -134
- package/src/lib/project/detect.js +0 -47
- package/src/lib/project/detect.test.js +0 -157
- package/src/lib/project/index.js +0 -56
- package/src/lib/project/output-paths.js +0 -87
- package/src/lib/project/output-paths.test.js +0 -66
- package/src/lib/project/paths.js +0 -272
- package/src/lib/project/version.js +0 -110
- package/src/lib/project/version.test.js +0 -350
- package/src/lib/reporter/index.test.js +0 -862
- package/src/main.spec.js +0 -61
- package/src/main.test.js +0 -347
- package/src/validators/validate-data-files.js +0 -154
- package/src/validators/validate-data-files.test.js +0 -217
|
@@ -1,282 +0,0 @@
|
|
|
1
|
-
import test from 'ava'
|
|
2
|
-
import sinon from 'sinon'
|
|
3
|
-
import esmock from 'esmock'
|
|
4
|
-
import path from 'node:path'
|
|
5
|
-
|
|
6
|
-
const TWO_MINUTES = 120000
|
|
7
|
-
const TEN_MINUTES = 600000
|
|
8
|
-
const ONE_HOUR = 3600000
|
|
9
|
-
const TWO_HOURS = 7200000
|
|
10
|
-
|
|
11
|
-
const configMock = { default: { get: () => 'HOURLY' } }
|
|
12
|
-
const buildStatusMock = { getStatus: () => undefined }
|
|
13
|
-
|
|
14
|
-
test.beforeEach((t) => {
|
|
15
|
-
t.context.sandbox = sinon.createSandbox()
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
test.afterEach.always((t) => {
|
|
19
|
-
t.context.sandbox.restore()
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
test('checkStaleBuild returns N/A when no _site directory exists', async (t) => {
|
|
23
|
-
const { sandbox } = t.context
|
|
24
|
-
|
|
25
|
-
const { checkStaleBuild } = await esmock('./stale-build.js', {
|
|
26
|
-
'node:fs': {
|
|
27
|
-
existsSync: sandbox.stub().returns(false),
|
|
28
|
-
},
|
|
29
|
-
'#lib/conf/config.js': configMock,
|
|
30
|
-
'#lib/conf/build-status.js': buildStatusMock,
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
const result = checkStaleBuild()
|
|
34
|
-
|
|
35
|
-
t.true(result.ok)
|
|
36
|
-
t.is(result.level, 'na', 'should return N/A level when no build output')
|
|
37
|
-
t.regex(result.message, /No build output found/)
|
|
38
|
-
t.truthy(result.remediation)
|
|
39
|
-
t.regex(result.remediation, /quire build/)
|
|
40
|
-
t.regex(result.remediation, /failed/)
|
|
41
|
-
t.truthy(result.docsUrl)
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
test('checkStaleBuild returns failure when last build failed and no _site exists', async (t) => {
|
|
45
|
-
const { sandbox } = t.context
|
|
46
|
-
|
|
47
|
-
const { checkStaleBuild } = await esmock('./stale-build.js', {
|
|
48
|
-
'node:fs': {
|
|
49
|
-
existsSync: sandbox.stub().returns(false),
|
|
50
|
-
},
|
|
51
|
-
'#lib/conf/config.js': configMock,
|
|
52
|
-
'#lib/conf/build-status.js': {
|
|
53
|
-
getStatus: () => ({ status: 'failed', timestamp: Date.now() }),
|
|
54
|
-
},
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
const result = checkStaleBuild()
|
|
58
|
-
|
|
59
|
-
t.false(result.ok)
|
|
60
|
-
t.is(result.level, undefined)
|
|
61
|
-
t.regex(result.message, /Last build failed/)
|
|
62
|
-
t.truthy(result.remediation)
|
|
63
|
-
t.regex(result.remediation, /quire build --debug/)
|
|
64
|
-
t.truthy(result.docsUrl)
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
test('checkStaleBuild returns ok when build is up to date', async (t) => {
|
|
68
|
-
const { sandbox } = t.context
|
|
69
|
-
|
|
70
|
-
const now = Date.now()
|
|
71
|
-
const existsSync = sandbox.stub()
|
|
72
|
-
existsSync.withArgs('_site').returns(true)
|
|
73
|
-
existsSync.returns(false) // source directories don't exist
|
|
74
|
-
|
|
75
|
-
const { checkStaleBuild } = await esmock('./stale-build.js', {
|
|
76
|
-
'node:fs': {
|
|
77
|
-
existsSync,
|
|
78
|
-
statSync: sandbox.stub().returns({ mtimeMs: now }),
|
|
79
|
-
readdirSync: sandbox.stub().returns([]),
|
|
80
|
-
},
|
|
81
|
-
'#lib/project/index.js': {
|
|
82
|
-
SOURCE_DIRECTORIES: ['content'],
|
|
83
|
-
},
|
|
84
|
-
'#lib/conf/config.js': configMock,
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
const result = checkStaleBuild()
|
|
88
|
-
|
|
89
|
-
t.true(result.ok)
|
|
90
|
-
t.regex(result.message, /up to date/)
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
test('checkStaleBuild returns warning when source is newer than build beyond threshold', async (t) => {
|
|
94
|
-
const { sandbox } = t.context
|
|
95
|
-
|
|
96
|
-
const buildTime = Date.now() - TWO_HOURS
|
|
97
|
-
const sourceTime = Date.now()
|
|
98
|
-
|
|
99
|
-
const existsSync = sandbox.stub()
|
|
100
|
-
existsSync.withArgs('_site').returns(true)
|
|
101
|
-
existsSync.withArgs('content').returns(true)
|
|
102
|
-
existsSync.returns(false)
|
|
103
|
-
|
|
104
|
-
// Use path.join for cross-platform compatibility
|
|
105
|
-
const filePath = path.join('content', 'file.md')
|
|
106
|
-
const statSync = sandbox.stub()
|
|
107
|
-
statSync.withArgs('_site').returns({ mtimeMs: buildTime })
|
|
108
|
-
statSync.withArgs(filePath).returns({ mtimeMs: sourceTime })
|
|
109
|
-
|
|
110
|
-
const { checkStaleBuild } = await esmock('./stale-build.js', {
|
|
111
|
-
'node:fs': {
|
|
112
|
-
existsSync,
|
|
113
|
-
statSync,
|
|
114
|
-
readdirSync: sandbox.stub().returns([
|
|
115
|
-
{ name: 'file.md', isDirectory: () => false },
|
|
116
|
-
]),
|
|
117
|
-
},
|
|
118
|
-
'#lib/project/index.js': {
|
|
119
|
-
SOURCE_DIRECTORIES: ['content'],
|
|
120
|
-
},
|
|
121
|
-
'#lib/conf/config.js': configMock,
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
const result = checkStaleBuild()
|
|
125
|
-
|
|
126
|
-
t.false(result.ok)
|
|
127
|
-
t.is(result.level, 'warn')
|
|
128
|
-
t.regex(result.message, /older than source files/)
|
|
129
|
-
t.truthy(result.remediation)
|
|
130
|
-
t.truthy(result.docsUrl)
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
test('checkStaleBuild returns ok when source is newer but within threshold', async (t) => {
|
|
134
|
-
const { sandbox } = t.context
|
|
135
|
-
|
|
136
|
-
const buildTime = Date.now() - TEN_MINUTES
|
|
137
|
-
const sourceTime = Date.now()
|
|
138
|
-
|
|
139
|
-
const existsSync = sandbox.stub()
|
|
140
|
-
existsSync.withArgs('_site').returns(true)
|
|
141
|
-
existsSync.withArgs('content').returns(true)
|
|
142
|
-
existsSync.returns(false)
|
|
143
|
-
|
|
144
|
-
const filePath = path.join('content', 'file.md')
|
|
145
|
-
const statSync = sandbox.stub()
|
|
146
|
-
statSync.withArgs('_site').returns({ mtimeMs: buildTime })
|
|
147
|
-
statSync.withArgs(filePath).returns({ mtimeMs: sourceTime })
|
|
148
|
-
|
|
149
|
-
const { checkStaleBuild } = await esmock('./stale-build.js', {
|
|
150
|
-
'node:fs': {
|
|
151
|
-
existsSync,
|
|
152
|
-
statSync,
|
|
153
|
-
readdirSync: sandbox.stub().returns([
|
|
154
|
-
{ name: 'file.md', isDirectory: () => false },
|
|
155
|
-
]),
|
|
156
|
-
},
|
|
157
|
-
'#lib/project/index.js': {
|
|
158
|
-
SOURCE_DIRECTORIES: ['content'],
|
|
159
|
-
},
|
|
160
|
-
'#lib/conf/config.js': configMock,
|
|
161
|
-
})
|
|
162
|
-
|
|
163
|
-
const result = checkStaleBuild()
|
|
164
|
-
|
|
165
|
-
t.true(result.ok)
|
|
166
|
-
t.regex(result.message, /up to date/)
|
|
167
|
-
})
|
|
168
|
-
|
|
169
|
-
test('checkStaleBuild respects ZERO threshold setting', async (t) => {
|
|
170
|
-
const { sandbox } = t.context
|
|
171
|
-
|
|
172
|
-
const buildTime = Date.now() - TWO_MINUTES
|
|
173
|
-
const sourceTime = Date.now()
|
|
174
|
-
|
|
175
|
-
const existsSync = sandbox.stub()
|
|
176
|
-
existsSync.withArgs('_site').returns(true)
|
|
177
|
-
existsSync.withArgs('content').returns(true)
|
|
178
|
-
existsSync.returns(false)
|
|
179
|
-
|
|
180
|
-
const filePath = path.join('content', 'file.md')
|
|
181
|
-
const statSync = sandbox.stub()
|
|
182
|
-
statSync.withArgs('_site').returns({ mtimeMs: buildTime })
|
|
183
|
-
statSync.withArgs(filePath).returns({ mtimeMs: sourceTime })
|
|
184
|
-
|
|
185
|
-
// ZERO = 0ms threshold — any difference triggers warning
|
|
186
|
-
const { checkStaleBuild } = await esmock('./stale-build.js', {
|
|
187
|
-
'node:fs': {
|
|
188
|
-
existsSync,
|
|
189
|
-
statSync,
|
|
190
|
-
readdirSync: sandbox.stub().returns([
|
|
191
|
-
{ name: 'file.md', isDirectory: () => false },
|
|
192
|
-
]),
|
|
193
|
-
},
|
|
194
|
-
'#lib/project/index.js': {
|
|
195
|
-
SOURCE_DIRECTORIES: ['content'],
|
|
196
|
-
},
|
|
197
|
-
'#lib/conf/config.js': {
|
|
198
|
-
default: { get: () => 'ZERO' },
|
|
199
|
-
},
|
|
200
|
-
})
|
|
201
|
-
|
|
202
|
-
const result = checkStaleBuild()
|
|
203
|
-
|
|
204
|
-
t.false(result.ok)
|
|
205
|
-
t.is(result.level, 'warn')
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
test('checkStaleBuild NEVER threshold disables stale warnings', async (t) => {
|
|
209
|
-
const { sandbox } = t.context
|
|
210
|
-
|
|
211
|
-
const buildTime = Date.now() - ONE_HOUR
|
|
212
|
-
const sourceTime = Date.now()
|
|
213
|
-
|
|
214
|
-
const existsSync = sandbox.stub()
|
|
215
|
-
existsSync.withArgs('_site').returns(true)
|
|
216
|
-
existsSync.withArgs('content').returns(true)
|
|
217
|
-
existsSync.returns(false)
|
|
218
|
-
|
|
219
|
-
const filePath = path.join('content', 'file.md')
|
|
220
|
-
const statSync = sandbox.stub()
|
|
221
|
-
statSync.withArgs('_site').returns({ mtimeMs: buildTime })
|
|
222
|
-
statSync.withArgs(filePath).returns({ mtimeMs: sourceTime })
|
|
223
|
-
|
|
224
|
-
const { checkStaleBuild } = await esmock('./stale-build.js', {
|
|
225
|
-
'node:fs': {
|
|
226
|
-
existsSync,
|
|
227
|
-
statSync,
|
|
228
|
-
readdirSync: sandbox.stub().returns([
|
|
229
|
-
{ name: 'file.md', isDirectory: () => false },
|
|
230
|
-
]),
|
|
231
|
-
},
|
|
232
|
-
'#lib/project/index.js': {
|
|
233
|
-
SOURCE_DIRECTORIES: ['content'],
|
|
234
|
-
},
|
|
235
|
-
'#lib/conf/config.js': {
|
|
236
|
-
default: { get: () => 'NEVER' },
|
|
237
|
-
},
|
|
238
|
-
})
|
|
239
|
-
|
|
240
|
-
const result = checkStaleBuild()
|
|
241
|
-
|
|
242
|
-
t.true(result.ok)
|
|
243
|
-
t.regex(result.message, /up to date/)
|
|
244
|
-
})
|
|
245
|
-
|
|
246
|
-
test('checkStaleBuild includes remediation with build commands', async (t) => {
|
|
247
|
-
const { sandbox } = t.context
|
|
248
|
-
|
|
249
|
-
const buildTime = Date.now() - TWO_HOURS
|
|
250
|
-
const sourceTime = Date.now()
|
|
251
|
-
|
|
252
|
-
const existsSync = sandbox.stub()
|
|
253
|
-
existsSync.withArgs('_site').returns(true)
|
|
254
|
-
existsSync.withArgs('content').returns(true)
|
|
255
|
-
existsSync.returns(false)
|
|
256
|
-
|
|
257
|
-
// Use path.join for cross-platform compatibility
|
|
258
|
-
const filePath = path.join('content', 'page.md')
|
|
259
|
-
const statSync = sandbox.stub()
|
|
260
|
-
statSync.withArgs('_site').returns({ mtimeMs: buildTime })
|
|
261
|
-
statSync.withArgs(filePath).returns({ mtimeMs: sourceTime })
|
|
262
|
-
|
|
263
|
-
const { checkStaleBuild } = await esmock('./stale-build.js', {
|
|
264
|
-
'node:fs': {
|
|
265
|
-
existsSync,
|
|
266
|
-
statSync,
|
|
267
|
-
readdirSync: sandbox.stub().returns([
|
|
268
|
-
{ name: 'page.md', isDirectory: () => false },
|
|
269
|
-
]),
|
|
270
|
-
},
|
|
271
|
-
'#lib/project/index.js': {
|
|
272
|
-
SOURCE_DIRECTORIES: ['content'],
|
|
273
|
-
},
|
|
274
|
-
'#lib/conf/config.js': configMock,
|
|
275
|
-
})
|
|
276
|
-
|
|
277
|
-
const result = checkStaleBuild()
|
|
278
|
-
|
|
279
|
-
t.false(result.ok)
|
|
280
|
-
t.regex(result.remediation, /quire build/)
|
|
281
|
-
t.regex(result.remediation, /quire preview/)
|
|
282
|
-
})
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Data files validation check
|
|
3
|
-
*
|
|
4
|
-
* @module lib/doctor/checks/project/data-files
|
|
5
|
-
*/
|
|
6
|
-
import { DATA_DIR } from '#lib/project/index.js'
|
|
7
|
-
import { validateDataFiles } from '#src/validators/validate-data-files.js'
|
|
8
|
-
import createDebug from '#debug'
|
|
9
|
-
import { DOCS_BASE_URL } from '#lib/doctor/constants.js'
|
|
10
|
-
|
|
11
|
-
const debug = createDebug('lib:doctor:data-files')
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Check data files in content/_data/ for YAML syntax and schema validation
|
|
15
|
-
*
|
|
16
|
-
* Validates:
|
|
17
|
-
* - Required files exist (publication.yaml)
|
|
18
|
-
* - YAML syntax is correct
|
|
19
|
-
* - Files conform to their JSON schemas (when schema exists)
|
|
20
|
-
* - No duplicate IDs in arrays
|
|
21
|
-
*
|
|
22
|
-
* @returns {import('../../index.js').CheckResult}
|
|
23
|
-
*/
|
|
24
|
-
export function checkDataFiles() {
|
|
25
|
-
const result = validateDataFiles()
|
|
26
|
-
|
|
27
|
-
// Not in a project directory
|
|
28
|
-
if (result.notInProject) {
|
|
29
|
-
debug('No %s directory found, skipping data files check', DATA_DIR)
|
|
30
|
-
return {
|
|
31
|
-
ok: true,
|
|
32
|
-
level: 'na',
|
|
33
|
-
message: `No ${DATA_DIR} directory (not in project)`,
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
debug('Found %d YAML files in %s', result.fileCount, DATA_DIR)
|
|
38
|
-
|
|
39
|
-
if (!result.valid) {
|
|
40
|
-
const issueCount = result.errors.length === 1 ? '1 issue' : `${result.errors.length} issues`
|
|
41
|
-
return {
|
|
42
|
-
ok: false,
|
|
43
|
-
level: 'warn',
|
|
44
|
-
message: `${issueCount} in data files`,
|
|
45
|
-
remediation: `Fix the following issues in ${DATA_DIR}:\n • ${result.errors.join('\n • ')}`,
|
|
46
|
-
docsUrl: `${DOCS_BASE_URL}/metadata-configuration/`,
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return {
|
|
51
|
-
ok: true,
|
|
52
|
-
message: `${result.fileCount} files validated`,
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export default checkDataFiles
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
import test from 'ava'
|
|
2
|
-
import sinon from 'sinon'
|
|
3
|
-
import esmock from 'esmock'
|
|
4
|
-
|
|
5
|
-
test.beforeEach((t) => {
|
|
6
|
-
t.context.sandbox = sinon.createSandbox()
|
|
7
|
-
})
|
|
8
|
-
|
|
9
|
-
test.afterEach.always((t) => {
|
|
10
|
-
t.context.sandbox.restore()
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
test('checkDataFiles returns N/A when no data directory exists', async (t) => {
|
|
14
|
-
const { sandbox } = t.context
|
|
15
|
-
|
|
16
|
-
const { checkDataFiles } = await esmock('./data-files.js', {
|
|
17
|
-
'#src/validators/validate-data-files.js': {
|
|
18
|
-
validateDataFiles: sandbox.stub().returns({
|
|
19
|
-
valid: true,
|
|
20
|
-
errors: [],
|
|
21
|
-
fileCount: 0,
|
|
22
|
-
files: [],
|
|
23
|
-
notInProject: true,
|
|
24
|
-
}),
|
|
25
|
-
},
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
const result = checkDataFiles()
|
|
29
|
-
|
|
30
|
-
t.true(result.ok)
|
|
31
|
-
t.is(result.level, 'na')
|
|
32
|
-
t.regex(result.message, /No content\/_data directory/)
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
test('checkDataFiles returns ok when all YAML files are valid', async (t) => {
|
|
36
|
-
const { sandbox } = t.context
|
|
37
|
-
|
|
38
|
-
const { checkDataFiles } = await esmock('./data-files.js', {
|
|
39
|
-
'#src/validators/validate-data-files.js': {
|
|
40
|
-
validateDataFiles: sandbox.stub().returns({
|
|
41
|
-
valid: true,
|
|
42
|
-
errors: [],
|
|
43
|
-
fileCount: 1,
|
|
44
|
-
files: [{ file: 'publication.yaml', valid: true, errors: [] }],
|
|
45
|
-
}),
|
|
46
|
-
},
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
const result = checkDataFiles()
|
|
50
|
-
|
|
51
|
-
t.true(result.ok)
|
|
52
|
-
t.regex(result.message, /1 files validated/)
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
test('checkDataFiles returns warning when required file is missing', async (t) => {
|
|
56
|
-
const { sandbox } = t.context
|
|
57
|
-
|
|
58
|
-
const { checkDataFiles } = await esmock('./data-files.js', {
|
|
59
|
-
'#src/validators/validate-data-files.js': {
|
|
60
|
-
validateDataFiles: sandbox.stub().returns({
|
|
61
|
-
valid: false,
|
|
62
|
-
errors: ['Required file missing: publication.yaml'],
|
|
63
|
-
fileCount: 0,
|
|
64
|
-
files: [],
|
|
65
|
-
}),
|
|
66
|
-
},
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
const result = checkDataFiles()
|
|
70
|
-
|
|
71
|
-
t.false(result.ok)
|
|
72
|
-
t.is(result.level, 'warn')
|
|
73
|
-
t.regex(result.message, /1 issue/)
|
|
74
|
-
t.regex(result.remediation, /publication\.yaml/)
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
test('checkDataFiles returns warning when YAML has syntax error', async (t) => {
|
|
78
|
-
const { sandbox } = t.context
|
|
79
|
-
|
|
80
|
-
const { checkDataFiles } = await esmock('./data-files.js', {
|
|
81
|
-
'#src/validators/validate-data-files.js': {
|
|
82
|
-
validateDataFiles: sandbox.stub().returns({
|
|
83
|
-
valid: false,
|
|
84
|
-
errors: ['invalid.yaml: YAML syntax error at line 1 - unexpected end of stream'],
|
|
85
|
-
fileCount: 2,
|
|
86
|
-
files: [
|
|
87
|
-
{ file: 'publication.yaml', valid: true, errors: [] },
|
|
88
|
-
{ file: 'invalid.yaml', valid: false, errors: ['invalid.yaml: YAML syntax error at line 1 - unexpected end of stream'] },
|
|
89
|
-
],
|
|
90
|
-
}),
|
|
91
|
-
},
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
const result = checkDataFiles()
|
|
95
|
-
|
|
96
|
-
t.false(result.ok)
|
|
97
|
-
t.is(result.level, 'warn')
|
|
98
|
-
t.regex(result.remediation, /invalid\.yaml/)
|
|
99
|
-
t.regex(result.remediation, /YAML syntax error/)
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
test('checkDataFiles returns warning when duplicate IDs exist', async (t) => {
|
|
103
|
-
const { sandbox } = t.context
|
|
104
|
-
|
|
105
|
-
const { checkDataFiles } = await esmock('./data-files.js', {
|
|
106
|
-
'#src/validators/validate-data-files.js': {
|
|
107
|
-
validateDataFiles: sandbox.stub().returns({
|
|
108
|
-
valid: false,
|
|
109
|
-
errors: ['figures.yaml: duplicate IDs found: fig-1'],
|
|
110
|
-
fileCount: 2,
|
|
111
|
-
files: [
|
|
112
|
-
{ file: 'publication.yaml', valid: true, errors: [] },
|
|
113
|
-
{ file: 'figures.yaml', valid: false, errors: ['figures.yaml: duplicate IDs found: fig-1'] },
|
|
114
|
-
],
|
|
115
|
-
}),
|
|
116
|
-
},
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
const result = checkDataFiles()
|
|
120
|
-
|
|
121
|
-
t.false(result.ok)
|
|
122
|
-
t.is(result.level, 'warn')
|
|
123
|
-
t.regex(result.remediation, /duplicate IDs/)
|
|
124
|
-
t.regex(result.remediation, /fig-1/)
|
|
125
|
-
})
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Dependencies check
|
|
3
|
-
*
|
|
4
|
-
* @module lib/doctor/checks/project/dependencies
|
|
5
|
-
*/
|
|
6
|
-
import fs from 'node:fs'
|
|
7
|
-
import path from 'node:path'
|
|
8
|
-
import createDebug from '#debug'
|
|
9
|
-
import { DOCS_BASE_URL } from '#lib/doctor/constants.js'
|
|
10
|
-
|
|
11
|
-
const debug = createDebug('lib:doctor:dependencies')
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Check if dependencies are installed
|
|
15
|
-
* @returns {import('../../index.js').CheckResult}
|
|
16
|
-
*/
|
|
17
|
-
export function checkDependencies() {
|
|
18
|
-
const hasPackageJson = fs.existsSync('package.json')
|
|
19
|
-
const hasNodeModules = fs.existsSync('node_modules')
|
|
20
|
-
|
|
21
|
-
debug('package.json exists: %s, node_modules exists: %s', hasPackageJson, hasNodeModules)
|
|
22
|
-
|
|
23
|
-
if (!hasPackageJson) {
|
|
24
|
-
return {
|
|
25
|
-
ok: true,
|
|
26
|
-
level: 'na',
|
|
27
|
-
message: 'No package.json (not in project directory)',
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (!hasNodeModules) {
|
|
32
|
-
return {
|
|
33
|
-
ok: false,
|
|
34
|
-
message: 'node_modules not found',
|
|
35
|
-
remediation: `Install project dependencies by running:
|
|
36
|
-
• npm install
|
|
37
|
-
|
|
38
|
-
If you continue to have issues:
|
|
39
|
-
• Delete node_modules folder and package-lock.json, then run npm install again
|
|
40
|
-
• Ensure you have write permissions in the project directory`,
|
|
41
|
-
docsUrl: `${DOCS_BASE_URL}/quire-commands/#install-dependencies`,
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const nodeModulesPath = path.resolve('node_modules')
|
|
46
|
-
return {
|
|
47
|
-
ok: true,
|
|
48
|
-
message: 'installed',
|
|
49
|
-
details: nodeModulesPath,
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export default checkDependencies
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import test from 'ava'
|
|
2
|
-
import sinon from 'sinon'
|
|
3
|
-
import esmock from 'esmock'
|
|
4
|
-
|
|
5
|
-
test.beforeEach((t) => {
|
|
6
|
-
t.context.sandbox = sinon.createSandbox()
|
|
7
|
-
})
|
|
8
|
-
|
|
9
|
-
test.afterEach.always((t) => {
|
|
10
|
-
t.context.sandbox.restore()
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
test('checkDependencies returns ok when node_modules exists', async (t) => {
|
|
14
|
-
const { sandbox } = t.context
|
|
15
|
-
|
|
16
|
-
const { checkDependencies } = await esmock('./dependencies.js', {
|
|
17
|
-
'node:fs': {
|
|
18
|
-
default: {
|
|
19
|
-
existsSync: sandbox.stub().returns(true),
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
'node:path': {
|
|
23
|
-
default: {
|
|
24
|
-
resolve: sandbox.stub().callsFake((p) => `/test/project/${p}`),
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
const result = checkDependencies()
|
|
30
|
-
|
|
31
|
-
t.true(result.ok)
|
|
32
|
-
t.is(result.message, 'installed')
|
|
33
|
-
t.is(result.details, '/test/project/node_modules')
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
test('checkDependencies returns not ok when node_modules missing', async (t) => {
|
|
37
|
-
const { sandbox } = t.context
|
|
38
|
-
|
|
39
|
-
const { checkDependencies } = await esmock('./dependencies.js', {
|
|
40
|
-
'node:fs': {
|
|
41
|
-
default: {
|
|
42
|
-
existsSync: sandbox.stub().callsFake((path) => path === 'package.json'),
|
|
43
|
-
},
|
|
44
|
-
},
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
const result = checkDependencies()
|
|
48
|
-
|
|
49
|
-
t.false(result.ok)
|
|
50
|
-
t.regex(result.message, /node_modules not found/)
|
|
51
|
-
t.truthy(result.remediation, 'should include remediation guidance')
|
|
52
|
-
t.truthy(result.docsUrl, 'should include documentation link')
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
test('checkDependencies returns N/A when no package.json (not in project)', async (t) => {
|
|
56
|
-
const { sandbox } = t.context
|
|
57
|
-
|
|
58
|
-
const { checkDependencies } = await esmock('./dependencies.js', {
|
|
59
|
-
'node:fs': {
|
|
60
|
-
default: {
|
|
61
|
-
existsSync: sandbox.stub().returns(false),
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
const result = checkDependencies()
|
|
67
|
-
|
|
68
|
-
t.true(result.ok)
|
|
69
|
-
t.is(result.level, 'na', 'should return N/A level when not in project')
|
|
70
|
-
t.regex(result.message, /No package\.json/)
|
|
71
|
-
})
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Project checks
|
|
3
|
-
*
|
|
4
|
-
* Checks for project structure and configuration.
|
|
5
|
-
*
|
|
6
|
-
* @module lib/doctor/checks/project
|
|
7
|
-
*/
|
|
8
|
-
export { checkQuireProject } from './quire-project.js'
|
|
9
|
-
export { checkDependencies } from './dependencies.js'
|
|
10
|
-
export { checkOutdatedQuire11ty } from './quire-11ty.js'
|
|
11
|
-
export { checkDataFiles } from './data-files.js'
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* quire-11ty version check
|
|
3
|
-
*
|
|
4
|
-
* @module lib/doctor/checks/project/quire-11ty
|
|
5
|
-
*/
|
|
6
|
-
import fs from 'node:fs'
|
|
7
|
-
import semver from 'semver'
|
|
8
|
-
import npm from '#lib/npm/index.js'
|
|
9
|
-
import config from '#lib/conf/config.js'
|
|
10
|
-
import createDebug from '#debug'
|
|
11
|
-
import { DOCS_BASE_URL, QUIRE_11TY_PACKAGE } from '#lib/doctor/constants.js'
|
|
12
|
-
|
|
13
|
-
const debug = createDebug('lib:doctor:quire-11ty')
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Check if quire-11ty is outdated compared to the latest available version
|
|
17
|
-
*
|
|
18
|
-
* Compares the installed version of @thegetty/quire-11ty against the latest
|
|
19
|
-
* version available on npm (using the configured updateChannel, e.g., 'rc').
|
|
20
|
-
*
|
|
21
|
-
* @returns {Promise<import('../../index.js').CheckResult>}
|
|
22
|
-
*/
|
|
23
|
-
export async function checkOutdatedQuire11ty() {
|
|
24
|
-
const packagePath = 'package.json'
|
|
25
|
-
|
|
26
|
-
// Skip if not in a project directory
|
|
27
|
-
if (!fs.existsSync(packagePath)) {
|
|
28
|
-
debug('package.json not found, skipping outdated check')
|
|
29
|
-
return {
|
|
30
|
-
ok: true,
|
|
31
|
-
level: 'na',
|
|
32
|
-
message: 'quire-11ty not installed (not in project)',
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Read installed version from the project's own package.json
|
|
37
|
-
// Nota bene: in Quire projects, quire-11ty IS the project itself
|
|
38
|
-
// (its files are copied into the project root by `quire new`)
|
|
39
|
-
let installedVersion
|
|
40
|
-
try {
|
|
41
|
-
const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'))
|
|
42
|
-
if (packageJson.name !== QUIRE_11TY_PACKAGE) {
|
|
43
|
-
debug('package.json is not a quire-11ty project: %s', packageJson.name)
|
|
44
|
-
return {
|
|
45
|
-
ok: true,
|
|
46
|
-
level: 'na',
|
|
47
|
-
message: 'quire-11ty not installed (not in project)',
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
installedVersion = packageJson.version
|
|
51
|
-
debug('Installed quire-11ty version: %s', installedVersion)
|
|
52
|
-
} catch (error) {
|
|
53
|
-
debug('Failed to read installed quire-11ty version: %s', error.message)
|
|
54
|
-
return {
|
|
55
|
-
ok: false,
|
|
56
|
-
level: 'warn',
|
|
57
|
-
message: 'Could not read installed quire-11ty version',
|
|
58
|
-
remediation: `Check that ${QUIRE_11TY_PACKAGE} is properly installed:
|
|
59
|
-
• Run "npm install" to reinstall dependencies
|
|
60
|
-
• If the issue persists, delete node_modules and reinstall`,
|
|
61
|
-
docsUrl: `${DOCS_BASE_URL}/quire-commands/#install-dependencies`,
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Fetch latest version from npm registry
|
|
66
|
-
const updateChannel = config.get('updateChannel') || 'rc'
|
|
67
|
-
let latestVersion
|
|
68
|
-
try {
|
|
69
|
-
latestVersion = await npm.view(QUIRE_11TY_PACKAGE, `dist-tags.${updateChannel}`)
|
|
70
|
-
debug('Latest quire-11ty version (%s channel): %s', updateChannel, latestVersion)
|
|
71
|
-
} catch (error) {
|
|
72
|
-
debug('Failed to fetch latest quire-11ty version: %s', error.message)
|
|
73
|
-
return {
|
|
74
|
-
ok: true,
|
|
75
|
-
message: `v${installedVersion} (could not check for updates)`,
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Compare versions
|
|
80
|
-
if (semver.lt(installedVersion, latestVersion)) {
|
|
81
|
-
return {
|
|
82
|
-
ok: false,
|
|
83
|
-
level: 'warn',
|
|
84
|
-
message: `v${installedVersion} installed, v${latestVersion} available`,
|
|
85
|
-
remediation: `Update quire-11ty to the latest version:
|
|
86
|
-
• Run "quire use ${latestVersion}" to update
|
|
87
|
-
• Then run "npm install" to install the new version`,
|
|
88
|
-
docsUrl: `${DOCS_BASE_URL}/quire-commands/#update-quire`,
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return {
|
|
93
|
-
ok: true,
|
|
94
|
-
message: `v${installedVersion} (up to date)`,
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export default checkOutdatedQuire11ty
|