@zeropress/build-pages 0.5.1 → 0.5.4
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 +171 -43
- package/action.yml +6 -6
- package/dist/action.js +37 -5
- package/dist/prebuild.js +4629 -0
- package/package.json +3 -2
- package/schemas/zeropress-build-pages.config.v0.1.schema.json +2 -62
- package/src/action.js +2 -2
- package/src/index.js +65 -24
- package/src/prebuild.js +338 -53
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeropress/build-pages",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.4",
|
|
4
4
|
"description": "ZeroPress Markdown build action and CLI for static hosting",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -40,7 +40,8 @@
|
|
|
40
40
|
"node": ">=18.18.0"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@zeropress/build": "0.5.1"
|
|
43
|
+
"@zeropress/build": "0.5.1",
|
|
44
|
+
"gray-matter": "4.0.3"
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
46
47
|
"esbuild": "0.28.0"
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
"site": {
|
|
36
36
|
"type": "object",
|
|
37
37
|
"additionalProperties": false,
|
|
38
|
-
"description": "
|
|
39
|
-
"markdownDescription": "
|
|
38
|
+
"description": "User-facing site metadata used by Build Pages.",
|
|
39
|
+
"markdownDescription": "User-facing site metadata used by Build Pages.",
|
|
40
40
|
"properties": {
|
|
41
41
|
"title": {
|
|
42
42
|
"type": "string",
|
|
@@ -50,37 +50,8 @@
|
|
|
50
50
|
"description": "Canonical site URL. Use an empty string or omit this field for local builds without canonical output.",
|
|
51
51
|
"markdownDescription": "Canonical site URL. Use an empty string or omit this field for local builds without canonical output."
|
|
52
52
|
},
|
|
53
|
-
"mediaBaseUrl": {
|
|
54
|
-
"type": "string"
|
|
55
|
-
},
|
|
56
|
-
"locale": {
|
|
57
|
-
"type": "string",
|
|
58
|
-
"minLength": 1
|
|
59
|
-
},
|
|
60
|
-
"postsPerPage": {
|
|
61
|
-
"type": "integer",
|
|
62
|
-
"minimum": 1
|
|
63
|
-
},
|
|
64
|
-
"dateFormat": {
|
|
65
|
-
"type": "string",
|
|
66
|
-
"minLength": 1
|
|
67
|
-
},
|
|
68
|
-
"timeFormat": {
|
|
69
|
-
"type": "string",
|
|
70
|
-
"minLength": 1
|
|
71
|
-
},
|
|
72
|
-
"timezone": {
|
|
73
|
-
"type": "string",
|
|
74
|
-
"minLength": 1
|
|
75
|
-
},
|
|
76
|
-
"permalinks": {
|
|
77
|
-
"$ref": "#/$defs/permalinks"
|
|
78
|
-
},
|
|
79
53
|
"footer": {
|
|
80
54
|
"$ref": "#/$defs/siteFooter"
|
|
81
|
-
},
|
|
82
|
-
"disallowComments": {
|
|
83
|
-
"type": "boolean"
|
|
84
55
|
}
|
|
85
56
|
}
|
|
86
57
|
},
|
|
@@ -114,37 +85,6 @@
|
|
|
114
85
|
}
|
|
115
86
|
}
|
|
116
87
|
},
|
|
117
|
-
"permalinks": {
|
|
118
|
-
"type": "object",
|
|
119
|
-
"additionalProperties": false,
|
|
120
|
-
"description": "Preview-data permalink defaults emitted by Build Pages.",
|
|
121
|
-
"markdownDescription": "Preview-data permalink defaults emitted by Build Pages.",
|
|
122
|
-
"properties": {
|
|
123
|
-
"output_style": {
|
|
124
|
-
"type": "string",
|
|
125
|
-
"enum": ["directory", "html-extension"]
|
|
126
|
-
},
|
|
127
|
-
"posts": {
|
|
128
|
-
"$ref": "#/$defs/permalinkPattern"
|
|
129
|
-
},
|
|
130
|
-
"pages": {
|
|
131
|
-
"$ref": "#/$defs/permalinkPattern"
|
|
132
|
-
},
|
|
133
|
-
"categories": {
|
|
134
|
-
"$ref": "#/$defs/permalinkPattern"
|
|
135
|
-
},
|
|
136
|
-
"tags": {
|
|
137
|
-
"$ref": "#/$defs/permalinkPattern"
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
},
|
|
141
|
-
"permalinkPattern": {
|
|
142
|
-
"type": "string",
|
|
143
|
-
"minLength": 1,
|
|
144
|
-
"pattern": "^/",
|
|
145
|
-
"description": "Absolute URL path pattern passed through to preview-data site.permalinks.",
|
|
146
|
-
"markdownDescription": "Absolute URL path pattern passed through to preview-data `site.permalinks`."
|
|
147
|
-
},
|
|
148
88
|
"frontPage": {
|
|
149
89
|
"type": "object",
|
|
150
90
|
"additionalProperties": false,
|
package/src/action.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { runBuildPages } from './index.js';
|
|
2
2
|
|
|
3
3
|
const options = {
|
|
4
|
-
source: input('source') || './',
|
|
4
|
+
source: input('source') || './docs',
|
|
5
5
|
destination: input('destination') || './_site',
|
|
6
6
|
theme: input('theme') || 'docs',
|
|
7
7
|
themePath: input('theme-path'),
|
|
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
12
|
};
|
|
13
13
|
|
|
14
14
|
try {
|
package/src/index.js
CHANGED
|
@@ -7,9 +7,11 @@ 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
|
-
const STAGING_DIR = '.zeropress/
|
|
14
|
+
const STAGING_DIR = '.zeropress/public-assets';
|
|
13
15
|
const DEFAULT_THEME = 'docs';
|
|
14
16
|
|
|
15
17
|
export async function runCli(argv = process.argv.slice(2)) {
|
|
@@ -42,6 +44,13 @@ export async function runBuildPages(options) {
|
|
|
42
44
|
const previewDataPath = path.join(cwd, PREVIEW_DATA_PATH);
|
|
43
45
|
const themeDir = resolveThemeDir(cwd, options);
|
|
44
46
|
|
|
47
|
+
assertBuildPagesPathLayout({
|
|
48
|
+
cwd,
|
|
49
|
+
sourceDir,
|
|
50
|
+
destinationDir,
|
|
51
|
+
themeDir,
|
|
52
|
+
generatedDir,
|
|
53
|
+
});
|
|
45
54
|
await assertDirectory(sourceDir, 'Source directory');
|
|
46
55
|
await fs.rm(generatedDir, { recursive: true, force: true });
|
|
47
56
|
await fs.mkdir(generatedDir, { recursive: true });
|
|
@@ -93,7 +102,7 @@ export async function runBuildPages(options) {
|
|
|
93
102
|
}
|
|
94
103
|
}
|
|
95
104
|
|
|
96
|
-
if (options.
|
|
105
|
+
if (!options.skipLinkCheck) {
|
|
97
106
|
const result = await checkInternalLinks(destinationDir);
|
|
98
107
|
if (result.brokenLinks.length) {
|
|
99
108
|
console.warn('Warning: broken internal links found:');
|
|
@@ -105,7 +114,7 @@ export async function runBuildPages(options) {
|
|
|
105
114
|
}
|
|
106
115
|
}
|
|
107
116
|
|
|
108
|
-
export function parseArgs(argv
|
|
117
|
+
export function parseArgs(argv) {
|
|
109
118
|
const flags = {};
|
|
110
119
|
|
|
111
120
|
for (let index = 0; index < argv.length; index += 1) {
|
|
@@ -114,19 +123,14 @@ export function parseArgs(argv, env = process.env) {
|
|
|
114
123
|
flags.skipUntitledMarkdown = true;
|
|
115
124
|
continue;
|
|
116
125
|
}
|
|
117
|
-
if (arg === '--check
|
|
118
|
-
flags.
|
|
119
|
-
continue;
|
|
120
|
-
}
|
|
121
|
-
if (arg === '--no-check-links') {
|
|
122
|
-
flags.checkLinks = false;
|
|
126
|
+
if (arg === '--skip-link-check') {
|
|
127
|
+
flags.skipLinkCheck = true;
|
|
123
128
|
continue;
|
|
124
129
|
}
|
|
125
130
|
|
|
126
131
|
const valueOptions = new Set([
|
|
127
132
|
'--source',
|
|
128
133
|
'--destination',
|
|
129
|
-
'--out',
|
|
130
134
|
'--theme',
|
|
131
135
|
'--theme-path',
|
|
132
136
|
'--config',
|
|
@@ -145,15 +149,24 @@ export function parseArgs(argv, env = process.env) {
|
|
|
145
149
|
throw new Error(`Invalid arguments: unknown option ${arg}`);
|
|
146
150
|
}
|
|
147
151
|
|
|
152
|
+
const source = flags.source || '';
|
|
153
|
+
const destination = flags.destination || '';
|
|
154
|
+
if (!source) {
|
|
155
|
+
throw new Error('Invalid arguments: --source <dir> is required.');
|
|
156
|
+
}
|
|
157
|
+
if (!destination) {
|
|
158
|
+
throw new Error('Invalid arguments: --destination <dir> is required.');
|
|
159
|
+
}
|
|
160
|
+
|
|
148
161
|
return {
|
|
149
|
-
source
|
|
150
|
-
destination
|
|
162
|
+
source,
|
|
163
|
+
destination,
|
|
151
164
|
theme: flags.theme || DEFAULT_THEME,
|
|
152
|
-
themePath: flags['theme-path'] ||
|
|
153
|
-
config: flags.config ||
|
|
154
|
-
siteUrl: flags['site-url'] ||
|
|
155
|
-
skipUntitledMarkdown: flags.skipUntitledMarkdown
|
|
156
|
-
|
|
165
|
+
themePath: flags['theme-path'] || '',
|
|
166
|
+
config: flags.config || '',
|
|
167
|
+
siteUrl: flags['site-url'] || '',
|
|
168
|
+
skipUntitledMarkdown: flags.skipUntitledMarkdown === true,
|
|
169
|
+
skipLinkCheck: flags.skipLinkCheck === true,
|
|
157
170
|
};
|
|
158
171
|
}
|
|
159
172
|
|
|
@@ -164,16 +177,14 @@ Usage:
|
|
|
164
177
|
zeropress-build-pages [options]
|
|
165
178
|
|
|
166
179
|
Options:
|
|
167
|
-
--source <dir>
|
|
168
|
-
--destination <dir> Output directory (
|
|
169
|
-
--out <dir> Alias for --destination
|
|
180
|
+
--source <dir> Dedicated source directory (required)
|
|
181
|
+
--destination <dir> Output directory (required)
|
|
170
182
|
--theme docs Bundled theme name (default: docs)
|
|
171
183
|
--theme-path <dir> Custom ZeroPress theme directory
|
|
172
184
|
--config <path> Config file (default: <source>/.zeropress/config.json)
|
|
173
185
|
--site-url <url> Canonical site URL override
|
|
174
|
-
--skip-untitled-markdown Skip Markdown files without
|
|
175
|
-
--check
|
|
176
|
-
--no-check-links Skip internal link checking
|
|
186
|
+
--skip-untitled-markdown Skip Markdown files without a page title
|
|
187
|
+
--skip-link-check Skip internal link checking
|
|
177
188
|
--help, -h Show help
|
|
178
189
|
--version, -v Show version`);
|
|
179
190
|
}
|
|
@@ -203,6 +214,32 @@ async function assertDirectory(dir, label) {
|
|
|
203
214
|
}
|
|
204
215
|
}
|
|
205
216
|
|
|
217
|
+
function assertBuildPagesPathLayout({ cwd, sourceDir, destinationDir, themeDir, generatedDir }) {
|
|
218
|
+
if (samePath(sourceDir, cwd)) {
|
|
219
|
+
throw new Error(
|
|
220
|
+
'Source directory must be a dedicated content directory, not the current working directory. '
|
|
221
|
+
+ `Received: ${formatPath(cwd, sourceDir)}`,
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
assertNoPathOverlap(cwd, 'Source directory', sourceDir, 'internal .zeropress working directory', generatedDir);
|
|
226
|
+
assertNoPathOverlap(cwd, 'Destination directory', destinationDir, 'internal .zeropress working directory', generatedDir);
|
|
227
|
+
assertNoPathOverlap(cwd, 'Theme directory', themeDir, 'internal .zeropress working directory', generatedDir);
|
|
228
|
+
assertNoPathOverlap(cwd, 'Source directory', sourceDir, 'destination directory', destinationDir);
|
|
229
|
+
assertNoPathOverlap(cwd, 'Source directory', sourceDir, 'theme directory', themeDir);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function assertNoPathOverlap(cwd, firstLabel, firstPath, secondLabel, secondPath) {
|
|
233
|
+
if (!pathsOverlap(firstPath, secondPath)) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
throw new Error(
|
|
237
|
+
`${firstLabel} must not overlap the ${secondLabel}. `
|
|
238
|
+
+ `${firstLabel}: ${formatPath(cwd, firstPath)}; `
|
|
239
|
+
+ `${secondLabel}: ${formatPath(cwd, secondPath)}`,
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
206
243
|
async function copyPublicStaging(sourceDir, targetDir, options) {
|
|
207
244
|
const entries = await fs.readdir(sourceDir, { withFileTypes: true });
|
|
208
245
|
|
|
@@ -254,6 +291,10 @@ function pathsOverlap(firstPath, secondPath) {
|
|
|
254
291
|
return first === second || isPathInside(first, second) || isPathInside(second, first);
|
|
255
292
|
}
|
|
256
293
|
|
|
294
|
+
function samePath(firstPath, secondPath) {
|
|
295
|
+
return path.resolve(firstPath) === path.resolve(secondPath);
|
|
296
|
+
}
|
|
297
|
+
|
|
257
298
|
function isPathInside(parentPath, childPath) {
|
|
258
299
|
const relativePath = path.relative(parentPath, childPath);
|
|
259
300
|
return Boolean(relativePath) && !relativePath.startsWith('..') && !path.isAbsolute(relativePath);
|