@zeropress/build-pages 0.5.2 → 0.5.5
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 +82 -36
- package/action.yml +8 -4
- package/dist/action.js +47 -5
- package/dist/prebuild.js +4635 -0
- package/package.json +1 -1
- package/src/action.js +6 -1
- package/src/index.js +69 -17
- package/src/prebuild.js +10 -4
package/package.json
CHANGED
package/src/action.js
CHANGED
|
@@ -8,7 +8,8 @@ const options = {
|
|
|
8
8
|
config: input('config'),
|
|
9
9
|
siteUrl: input('site-url'),
|
|
10
10
|
skipUntitledMarkdown: booleanInput('skip-untitled-markdown', false),
|
|
11
|
-
|
|
11
|
+
skipLinkCheck: booleanInput('skip-link-check', false),
|
|
12
|
+
copyMarkdownSource: falseOnlyInput('copy-markdown-source'),
|
|
12
13
|
};
|
|
13
14
|
|
|
14
15
|
try {
|
|
@@ -30,3 +31,7 @@ function booleanInput(name, fallback) {
|
|
|
30
31
|
}
|
|
31
32
|
return value.toLowerCase() === 'true';
|
|
32
33
|
}
|
|
34
|
+
|
|
35
|
+
function falseOnlyInput(name) {
|
|
36
|
+
return input(name).toLowerCase() !== 'false';
|
|
37
|
+
}
|
package/src/index.js
CHANGED
|
@@ -7,7 +7,9 @@ import { checkInternalLinks } from './check-links.js';
|
|
|
7
7
|
|
|
8
8
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
9
|
const packageDir = path.resolve(__dirname, '..');
|
|
10
|
-
const prebuildScript = path.join(packageDir, '
|
|
10
|
+
const prebuildScript = __dirname === path.join(packageDir, 'dist')
|
|
11
|
+
? path.join(__dirname, 'prebuild.js')
|
|
12
|
+
: path.join(packageDir, 'src', 'prebuild.js');
|
|
11
13
|
const PREVIEW_DATA_PATH = '.zeropress/preview-data.json';
|
|
12
14
|
const STAGING_DIR = '.zeropress/public-assets';
|
|
13
15
|
const DEFAULT_THEME = 'docs';
|
|
@@ -35,6 +37,7 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
35
37
|
|
|
36
38
|
export async function runBuildPages(options) {
|
|
37
39
|
const cwd = path.resolve(options.cwd || process.cwd());
|
|
40
|
+
const copyMarkdownSource = options.copyMarkdownSource !== false;
|
|
38
41
|
const sourceDir = path.resolve(cwd, options.source);
|
|
39
42
|
const destinationDir = path.resolve(cwd, options.destination);
|
|
40
43
|
const generatedDir = path.join(cwd, '.zeropress');
|
|
@@ -42,6 +45,13 @@ export async function runBuildPages(options) {
|
|
|
42
45
|
const previewDataPath = path.join(cwd, PREVIEW_DATA_PATH);
|
|
43
46
|
const themeDir = resolveThemeDir(cwd, options);
|
|
44
47
|
|
|
48
|
+
assertBuildPagesPathLayout({
|
|
49
|
+
cwd,
|
|
50
|
+
sourceDir,
|
|
51
|
+
destinationDir,
|
|
52
|
+
themeDir,
|
|
53
|
+
generatedDir,
|
|
54
|
+
});
|
|
45
55
|
await assertDirectory(sourceDir, 'Source directory');
|
|
46
56
|
await fs.rm(generatedDir, { recursive: true, force: true });
|
|
47
57
|
await fs.mkdir(generatedDir, { recursive: true });
|
|
@@ -51,6 +61,7 @@ export async function runBuildPages(options) {
|
|
|
51
61
|
ZEROPRESS_BUILD_PAGES_SOURCE: sourceDir,
|
|
52
62
|
ZEROPRESS_PUBLIC_DIR: sourceDir,
|
|
53
63
|
ZEROPRESS_SKIP_UNTITLED_MARKDOWN: String(Boolean(options.skipUntitledMarkdown)),
|
|
64
|
+
ZEROPRESS_COPY_MARKDOWN_SOURCE: String(copyMarkdownSource),
|
|
54
65
|
};
|
|
55
66
|
if (options.config) {
|
|
56
67
|
env.ZEROPRESS_BUILD_PAGES_CONFIG = path.resolve(cwd, options.config);
|
|
@@ -76,6 +87,7 @@ export async function runBuildPages(options) {
|
|
|
76
87
|
await fs.mkdir(stagingDir, { recursive: true });
|
|
77
88
|
await copyPublicStaging(sourceDir, stagingDir, {
|
|
78
89
|
excludePaths: [destinationDir, themeDir, generatedDir],
|
|
90
|
+
copyMarkdownSource,
|
|
79
91
|
});
|
|
80
92
|
|
|
81
93
|
const previousPublicDir = process.env.ZEROPRESS_PUBLIC_DIR;
|
|
@@ -93,7 +105,7 @@ export async function runBuildPages(options) {
|
|
|
93
105
|
}
|
|
94
106
|
}
|
|
95
107
|
|
|
96
|
-
if (options.
|
|
108
|
+
if (!options.skipLinkCheck) {
|
|
97
109
|
const result = await checkInternalLinks(destinationDir);
|
|
98
110
|
if (result.brokenLinks.length) {
|
|
99
111
|
console.warn('Warning: broken internal links found:');
|
|
@@ -105,7 +117,7 @@ export async function runBuildPages(options) {
|
|
|
105
117
|
}
|
|
106
118
|
}
|
|
107
119
|
|
|
108
|
-
export function parseArgs(argv
|
|
120
|
+
export function parseArgs(argv) {
|
|
109
121
|
const flags = {};
|
|
110
122
|
|
|
111
123
|
for (let index = 0; index < argv.length; index += 1) {
|
|
@@ -114,8 +126,12 @@ export function parseArgs(argv, env = process.env) {
|
|
|
114
126
|
flags.skipUntitledMarkdown = true;
|
|
115
127
|
continue;
|
|
116
128
|
}
|
|
117
|
-
if (arg === '--
|
|
118
|
-
flags.
|
|
129
|
+
if (arg === '--skip-link-check') {
|
|
130
|
+
flags.skipLinkCheck = true;
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
if (arg === '--no-copy-markdown-source') {
|
|
134
|
+
flags.copyMarkdownSource = false;
|
|
119
135
|
continue;
|
|
120
136
|
}
|
|
121
137
|
|
|
@@ -140,24 +156,25 @@ export function parseArgs(argv, env = process.env) {
|
|
|
140
156
|
throw new Error(`Invalid arguments: unknown option ${arg}`);
|
|
141
157
|
}
|
|
142
158
|
|
|
143
|
-
const source = flags.source ||
|
|
144
|
-
const destination = flags.destination ||
|
|
159
|
+
const source = flags.source || '';
|
|
160
|
+
const destination = flags.destination || '';
|
|
145
161
|
if (!source) {
|
|
146
|
-
throw new Error('Invalid arguments: --source <dir> is required.
|
|
162
|
+
throw new Error('Invalid arguments: --source <dir> is required.');
|
|
147
163
|
}
|
|
148
164
|
if (!destination) {
|
|
149
|
-
throw new Error('Invalid arguments: --destination <dir> is required.
|
|
165
|
+
throw new Error('Invalid arguments: --destination <dir> is required.');
|
|
150
166
|
}
|
|
151
167
|
|
|
152
168
|
return {
|
|
153
169
|
source,
|
|
154
170
|
destination,
|
|
155
171
|
theme: flags.theme || DEFAULT_THEME,
|
|
156
|
-
themePath: flags['theme-path'] ||
|
|
157
|
-
config: flags.config ||
|
|
158
|
-
siteUrl: flags['site-url'] ||
|
|
159
|
-
skipUntitledMarkdown: flags.skipUntitledMarkdown
|
|
160
|
-
|
|
172
|
+
themePath: flags['theme-path'] || '',
|
|
173
|
+
config: flags.config || '',
|
|
174
|
+
siteUrl: flags['site-url'] || '',
|
|
175
|
+
skipUntitledMarkdown: flags.skipUntitledMarkdown === true,
|
|
176
|
+
skipLinkCheck: flags.skipLinkCheck === true,
|
|
177
|
+
copyMarkdownSource: flags.copyMarkdownSource !== false,
|
|
161
178
|
};
|
|
162
179
|
}
|
|
163
180
|
|
|
@@ -168,14 +185,15 @@ Usage:
|
|
|
168
185
|
zeropress-build-pages [options]
|
|
169
186
|
|
|
170
187
|
Options:
|
|
171
|
-
--source <dir>
|
|
172
|
-
--destination <dir> Output directory (required
|
|
188
|
+
--source <dir> Dedicated source directory (required)
|
|
189
|
+
--destination <dir> Output directory (required)
|
|
173
190
|
--theme docs Bundled theme name (default: docs)
|
|
174
191
|
--theme-path <dir> Custom ZeroPress theme directory
|
|
175
192
|
--config <path> Config file (default: <source>/.zeropress/config.json)
|
|
176
193
|
--site-url <url> Canonical site URL override
|
|
177
194
|
--skip-untitled-markdown Skip Markdown files without a page title
|
|
178
|
-
--
|
|
195
|
+
--skip-link-check Skip internal link checking
|
|
196
|
+
--no-copy-markdown-source Do not copy original Markdown files to output
|
|
179
197
|
--help, -h Show help
|
|
180
198
|
--version, -v Show version`);
|
|
181
199
|
}
|
|
@@ -205,6 +223,32 @@ async function assertDirectory(dir, label) {
|
|
|
205
223
|
}
|
|
206
224
|
}
|
|
207
225
|
|
|
226
|
+
function assertBuildPagesPathLayout({ cwd, sourceDir, destinationDir, themeDir, generatedDir }) {
|
|
227
|
+
if (samePath(sourceDir, cwd)) {
|
|
228
|
+
throw new Error(
|
|
229
|
+
'Source directory must be a dedicated content directory, not the current working directory. '
|
|
230
|
+
+ `Received: ${formatPath(cwd, sourceDir)}`,
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
assertNoPathOverlap(cwd, 'Source directory', sourceDir, 'internal .zeropress working directory', generatedDir);
|
|
235
|
+
assertNoPathOverlap(cwd, 'Destination directory', destinationDir, 'internal .zeropress working directory', generatedDir);
|
|
236
|
+
assertNoPathOverlap(cwd, 'Theme directory', themeDir, 'internal .zeropress working directory', generatedDir);
|
|
237
|
+
assertNoPathOverlap(cwd, 'Source directory', sourceDir, 'destination directory', destinationDir);
|
|
238
|
+
assertNoPathOverlap(cwd, 'Source directory', sourceDir, 'theme directory', themeDir);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function assertNoPathOverlap(cwd, firstLabel, firstPath, secondLabel, secondPath) {
|
|
242
|
+
if (!pathsOverlap(firstPath, secondPath)) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
throw new Error(
|
|
246
|
+
`${firstLabel} must not overlap the ${secondLabel}. `
|
|
247
|
+
+ `${firstLabel}: ${formatPath(cwd, firstPath)}; `
|
|
248
|
+
+ `${secondLabel}: ${formatPath(cwd, secondPath)}`,
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
|
|
208
252
|
async function copyPublicStaging(sourceDir, targetDir, options) {
|
|
209
253
|
const entries = await fs.readdir(sourceDir, { withFileTypes: true });
|
|
210
254
|
|
|
@@ -229,6 +273,10 @@ async function copyPublicStaging(sourceDir, targetDir, options) {
|
|
|
229
273
|
continue;
|
|
230
274
|
}
|
|
231
275
|
|
|
276
|
+
if (options.copyMarkdownSource === false && entry.name.toLowerCase().endsWith('.md')) {
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
|
|
232
280
|
await fs.mkdir(path.dirname(targetPath), { recursive: true });
|
|
233
281
|
await fs.copyFile(sourcePath, targetPath);
|
|
234
282
|
}
|
|
@@ -256,6 +304,10 @@ function pathsOverlap(firstPath, secondPath) {
|
|
|
256
304
|
return first === second || isPathInside(first, second) || isPathInside(second, first);
|
|
257
305
|
}
|
|
258
306
|
|
|
307
|
+
function samePath(firstPath, secondPath) {
|
|
308
|
+
return path.resolve(firstPath) === path.resolve(secondPath);
|
|
309
|
+
}
|
|
310
|
+
|
|
259
311
|
function isPathInside(parentPath, childPath) {
|
|
260
312
|
const relativePath = path.relative(parentPath, childPath);
|
|
261
313
|
return Boolean(relativePath) && !relativePath.startsWith('..') && !path.isAbsolute(relativePath);
|
package/src/prebuild.js
CHANGED
|
@@ -5,7 +5,7 @@ import matter from 'gray-matter';
|
|
|
5
5
|
|
|
6
6
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
7
|
const rootDir = process.cwd();
|
|
8
|
-
const sourceDir = resolveEnvPath(['ZEROPRESS_BUILD_PAGES_SOURCE'
|
|
8
|
+
const sourceDir = resolveEnvPath(['ZEROPRESS_BUILD_PAGES_SOURCE'], 'docs');
|
|
9
9
|
const defaultConfigPath = path.join(sourceDir, '.zeropress', 'config.json');
|
|
10
10
|
const configPath = resolveOptionalEnvPath(['ZEROPRESS_BUILD_PAGES_CONFIG'], defaultConfigPath);
|
|
11
11
|
const outDir = path.join(rootDir, '.zeropress');
|
|
@@ -13,6 +13,7 @@ const buildPagesConfigPath = path.join(outDir, 'build-pages-config.json');
|
|
|
13
13
|
const previewDataPath = path.join(outDir, 'preview-data.json');
|
|
14
14
|
const buildReportPath = path.join(outDir, 'build-report.json');
|
|
15
15
|
const skipUntitledMarkdown = readBooleanEnv('ZEROPRESS_SKIP_UNTITLED_MARKDOWN');
|
|
16
|
+
const copyMarkdownSource = readBooleanEnv('ZEROPRESS_COPY_MARKDOWN_SOURCE', true);
|
|
16
17
|
const FRONT_PAGE_TYPES = new Set(['theme_index', 'markdown', 'html']);
|
|
17
18
|
const BUILD_PAGES_CONFIG_SCHEMA_URL = 'https://zeropress.dev/schemas/zeropress-build-pages.config.v0.1.schema.json';
|
|
18
19
|
const PREVIEW_DATA_SCHEMA_URL = 'https://zeropress.dev/schemas/preview-data.v0.5.schema.json';
|
|
@@ -96,7 +97,7 @@ async function main() {
|
|
|
96
97
|
path: route.path,
|
|
97
98
|
meta: {
|
|
98
99
|
...frontMatter.meta,
|
|
99
|
-
source_markdown_url: buildSourceMarkdownUrl(sourcePath),
|
|
100
|
+
...(copyMarkdownSource ? { source_markdown_url: buildSourceMarkdownUrl(sourcePath) } : {}),
|
|
100
101
|
},
|
|
101
102
|
content: rewriteMarkdownLinks(bodyMarkdown, sourcePath, routeBySourcePath),
|
|
102
103
|
document_type: 'markdown',
|
|
@@ -733,6 +734,7 @@ function buildPrebuildReport({
|
|
|
733
734
|
preview_data_path: formatSourcePath(previewDataPath),
|
|
734
735
|
report_path: formatSourcePath(buildReportPath),
|
|
735
736
|
skip_untitled_markdown: skipUntitledMarkdown,
|
|
737
|
+
copy_markdown_source: copyMarkdownSource,
|
|
736
738
|
markdown: {
|
|
737
739
|
discovered: sourceFiles.length,
|
|
738
740
|
generated_pages: pageInputs.length,
|
|
@@ -1305,8 +1307,12 @@ function readConfigInteger(value, fallback) {
|
|
|
1305
1307
|
return Number.isInteger(value) && value > 0 ? value : fallback;
|
|
1306
1308
|
}
|
|
1307
1309
|
|
|
1308
|
-
function readBooleanEnv(name) {
|
|
1309
|
-
|
|
1310
|
+
function readBooleanEnv(name, fallback = false) {
|
|
1311
|
+
const value = process.env[name]?.trim();
|
|
1312
|
+
if (!value) {
|
|
1313
|
+
return fallback;
|
|
1314
|
+
}
|
|
1315
|
+
return value.toLowerCase() === 'true';
|
|
1310
1316
|
}
|
|
1311
1317
|
|
|
1312
1318
|
function resolveEnvPath(names, fallback) {
|