ultimate-jekyll-manager 0.0.213 → 0.0.215
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/commands/setup.js
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
// Libraries
|
|
2
2
|
const Manager = new (require('../build.js'));
|
|
3
3
|
const logger = Manager.logger('setup');
|
|
4
|
-
const argv = Manager.getArguments();
|
|
5
4
|
const path = require('path');
|
|
6
5
|
const jetpack = require('fs-jetpack');
|
|
7
6
|
const version = require('wonderful-version');
|
|
8
7
|
const fetch = require('wonderful-fetch');
|
|
9
|
-
const { execute
|
|
8
|
+
const { execute } = require('node-powertools');
|
|
10
9
|
const NPM = require('npm-api');
|
|
11
10
|
const glob = require('glob').globSync;
|
|
12
|
-
const { minimatch } = require('minimatch');
|
|
13
11
|
const detectGitHubRepository = require('../gulp/tasks/utils/detect-github-repo');
|
|
14
12
|
const { Octokit } = require('@octokit/rest');
|
|
15
13
|
const sodium = require('libsodium-wrappers');
|
|
@@ -21,7 +19,7 @@ let config = Manager.getConfig('project');
|
|
|
21
19
|
const rootPathPackage = Manager.getRootPath('main');
|
|
22
20
|
const rootPathProject = Manager.getRootPath('project');
|
|
23
21
|
|
|
24
|
-
//
|
|
22
|
+
// Map of dependencies that should be installed as devDependencies
|
|
25
23
|
const DEPENDENCY_MAP = {
|
|
26
24
|
'gulp': 'dev',
|
|
27
25
|
}
|
|
@@ -31,17 +29,15 @@ module.exports = async function (options) {
|
|
|
31
29
|
options = options || {};
|
|
32
30
|
options.checkManager = options.checkManager !== 'false';
|
|
33
31
|
options.checkNode = options.checkNode !== 'false';
|
|
34
|
-
options.checkBundler = options.checkBundler !== 'false';
|
|
35
32
|
options.checkRuby = options.checkRuby !== 'false';
|
|
33
|
+
options.checkBundle = options.checkBundle !== 'false';
|
|
36
34
|
options.checkPeerDependencies = options.checkPeerDependencies !== 'false';
|
|
37
35
|
options.setupScripts = options.setupScripts !== 'false';
|
|
38
36
|
options.ensureCoreFiles = options.ensureCoreFiles !== 'false';
|
|
39
37
|
options.createCname = options.createCname !== 'false';
|
|
40
38
|
options.fetchFirebaseAuth = options.fetchFirebaseAuth !== 'false';
|
|
41
39
|
options.checkLocality = options.checkLocality !== 'false';
|
|
42
|
-
options.updateBundle = options.updateBundle !== 'false';
|
|
43
40
|
options.publishGitHubToken = options.publishGitHubToken !== 'false';
|
|
44
|
-
options.updateGitHubPages = options.updateGitHubPages !== 'false';
|
|
45
41
|
options.deduplicatePosts = options.deduplicatePosts !== 'false';
|
|
46
42
|
options.migrate = options.migrate !== 'false';
|
|
47
43
|
|
|
@@ -53,139 +49,114 @@ module.exports = async function (options) {
|
|
|
53
49
|
project.dependencies = project.dependencies || {};
|
|
54
50
|
project.devDependencies = project.devDependencies || {};
|
|
55
51
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
await logCWD();
|
|
59
|
-
|
|
60
|
-
// Run migrations
|
|
61
|
-
if (options.migrate) {
|
|
62
|
-
await migrate();
|
|
63
|
-
}
|
|
52
|
+
// Log current working directory
|
|
53
|
+
logger.log('Current working directory:', process.cwd());
|
|
64
54
|
|
|
65
|
-
|
|
66
|
-
|
|
55
|
+
// Run migrations
|
|
56
|
+
if (options.migrate) {
|
|
57
|
+
await migrate();
|
|
58
|
+
}
|
|
67
59
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
await updateManager();
|
|
71
|
-
}
|
|
60
|
+
// Detect GitHub repository early so it's available to all tasks/functions
|
|
61
|
+
await detectGitHubRepository(logger);
|
|
72
62
|
|
|
73
|
-
|
|
74
|
-
if (options.checkNode) {
|
|
75
|
-
await ensureNodeVersion();
|
|
76
|
-
}
|
|
63
|
+
// --- Version checks ---
|
|
77
64
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
65
|
+
// Ensure this package is up-to-date
|
|
66
|
+
if (options.checkManager) {
|
|
67
|
+
await ensureManagerVersion();
|
|
68
|
+
}
|
|
82
69
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
70
|
+
// Ensure proper node version
|
|
71
|
+
if (options.checkNode) {
|
|
72
|
+
await ensureNodeVersion();
|
|
73
|
+
}
|
|
87
74
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
75
|
+
// Ensure proper ruby version
|
|
76
|
+
if (options.checkRuby) {
|
|
77
|
+
await ensureRubyVersion();
|
|
78
|
+
}
|
|
92
79
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
80
|
+
// Ensure proper bundler version + install/update gems
|
|
81
|
+
if (options.checkBundle) {
|
|
82
|
+
await ensureBundle();
|
|
83
|
+
}
|
|
97
84
|
|
|
98
|
-
|
|
99
|
-
|
|
85
|
+
// Ensure peer dependencies are installed
|
|
86
|
+
if (options.checkPeerDependencies) {
|
|
87
|
+
await ensurePeerDependencies();
|
|
88
|
+
}
|
|
100
89
|
|
|
101
|
-
|
|
102
|
-
if (options.ensureCoreFiles) {
|
|
103
|
-
await ensureCoreFiles();
|
|
104
|
-
}
|
|
90
|
+
// --- Project setup ---
|
|
105
91
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
92
|
+
// Setup scripts in package.json
|
|
93
|
+
if (options.setupScripts) {
|
|
94
|
+
setupScripts();
|
|
95
|
+
}
|
|
110
96
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
97
|
+
// Ensure _config.yml exists
|
|
98
|
+
if (options.ensureCoreFiles) {
|
|
99
|
+
await ensureCoreFiles();
|
|
100
|
+
}
|
|
115
101
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
102
|
+
// Create CNAME file
|
|
103
|
+
if (options.createCname) {
|
|
104
|
+
createCname();
|
|
105
|
+
}
|
|
120
106
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
107
|
+
// Fetch firebase-auth files
|
|
108
|
+
if (options.fetchFirebaseAuth) {
|
|
109
|
+
await fetchFirebaseAuth();
|
|
110
|
+
}
|
|
125
111
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
112
|
+
// Warn if using local version
|
|
113
|
+
if (options.checkLocality) {
|
|
114
|
+
checkLocality();
|
|
115
|
+
}
|
|
130
116
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
117
|
+
// Publish GH_TOKEN as repository secret
|
|
118
|
+
if (options.publishGitHubToken) {
|
|
119
|
+
await publishGitHubToken();
|
|
120
|
+
}
|
|
135
121
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
} catch (e) {
|
|
141
|
-
// Throw error
|
|
142
|
-
throw e;
|
|
122
|
+
// Deduplicate posts (remove duplicate posts with same slug but different dates)
|
|
123
|
+
if (options.deduplicatePosts) {
|
|
124
|
+
await deduplicatePosts();
|
|
143
125
|
}
|
|
144
126
|
};
|
|
145
127
|
|
|
146
|
-
|
|
147
|
-
logger.log('Current working directory:', process.cwd());
|
|
148
|
-
// logger.log('Current working directory 2:', await execute('pwd'));
|
|
149
|
-
// logger.log('Current working directory 3:', await execute('ls -al'));
|
|
150
|
-
}
|
|
128
|
+
// --- Version check functions ---
|
|
151
129
|
|
|
152
|
-
async function
|
|
130
|
+
async function ensureManagerVersion() {
|
|
153
131
|
const npm = new NPM();
|
|
154
|
-
|
|
155
|
-
// Get the latest version
|
|
156
132
|
const installedVersion = project.devDependencies[package.name];
|
|
157
|
-
const latestVersion = await npm.repo(package.name)
|
|
158
|
-
.package()
|
|
159
|
-
.then((pkg) => {
|
|
160
|
-
return pkg.version;
|
|
161
|
-
}, (e) => {
|
|
162
|
-
return '0.0.0';
|
|
163
|
-
});
|
|
164
|
-
const isUpToDate = version.is(installedVersion, '>=', latestVersion);
|
|
165
|
-
const levelDifference = version.levelDifference(installedVersion, latestVersion);
|
|
166
133
|
|
|
167
|
-
// Check if installedVersion is truthy or throw error
|
|
168
134
|
if (!installedVersion) {
|
|
169
135
|
throw new Error(`No installed version of ${package.name} found in devDependencies.`);
|
|
170
136
|
}
|
|
171
137
|
|
|
172
|
-
//
|
|
173
|
-
logVersionCheck(package.name, installedVersion, latestVersion, isUpToDate);
|
|
174
|
-
|
|
175
|
-
// Quit if local
|
|
138
|
+
// Skip if local
|
|
176
139
|
if (installedVersion.startsWith('file:')) {
|
|
140
|
+
logVersionCheck(package.name, installedVersion, installedVersion, true);
|
|
177
141
|
return;
|
|
178
142
|
}
|
|
179
143
|
|
|
180
|
-
|
|
144
|
+
const latestVersion = await npm.repo(package.name)
|
|
145
|
+
.package()
|
|
146
|
+
.then((pkg) => pkg.version, () => '0.0.0');
|
|
147
|
+
|
|
148
|
+
const isUpToDate = version.is(installedVersion, '>=', latestVersion);
|
|
149
|
+
|
|
150
|
+
logVersionCheck(package.name, installedVersion, latestVersion, isUpToDate);
|
|
151
|
+
|
|
181
152
|
if (!isUpToDate) {
|
|
182
|
-
|
|
153
|
+
const levelDifference = version.levelDifference(installedVersion, latestVersion);
|
|
154
|
+
|
|
183
155
|
if (levelDifference === 'major' && installedVersion !== 'latest') {
|
|
184
156
|
return logger.error(`Major version difference detected. Please update to ${latestVersion} manually.`);
|
|
185
157
|
}
|
|
186
158
|
|
|
187
|
-
|
|
188
|
-
await install(package.name, latestVersion);
|
|
159
|
+
await npmInstall(package.name, latestVersion);
|
|
189
160
|
}
|
|
190
161
|
}
|
|
191
162
|
|
|
@@ -194,353 +165,235 @@ async function ensureNodeVersion() {
|
|
|
194
165
|
const requiredVersion = version.clean(package.engines.node);
|
|
195
166
|
const isUpToDate = version.is(installedVersion, '>=', requiredVersion);
|
|
196
167
|
|
|
197
|
-
// Log
|
|
198
168
|
logVersionCheck('Node.js', installedVersion, requiredVersion, isUpToDate);
|
|
199
169
|
|
|
200
|
-
// Check if we need to update
|
|
201
170
|
if (!isUpToDate) {
|
|
202
171
|
throw new Error(`Node version is out-of-date. Required version is ${requiredVersion}.`);
|
|
203
172
|
}
|
|
204
173
|
}
|
|
205
174
|
|
|
206
|
-
async function
|
|
175
|
+
async function ensureRubyVersion() {
|
|
207
176
|
const installedVersion = version.clean(
|
|
208
|
-
(await execute('
|
|
177
|
+
(await execute('ruby -v', { log: false })).match(/(\d+\.\d+\.\d+)/)[0]
|
|
209
178
|
);
|
|
210
|
-
const requiredVersion = version.clean(package.engines.
|
|
179
|
+
const requiredVersion = version.clean(package.engines.ruby);
|
|
211
180
|
const isUpToDate = version.is(installedVersion, '>=', requiredVersion);
|
|
212
181
|
|
|
213
|
-
|
|
214
|
-
logVersionCheck('Bundler', installedVersion, requiredVersion, isUpToDate);
|
|
182
|
+
logVersionCheck('Ruby', installedVersion, requiredVersion, isUpToDate);
|
|
215
183
|
|
|
216
|
-
// Check if we need to update
|
|
217
184
|
if (!isUpToDate) {
|
|
218
|
-
|
|
219
|
-
await execute(`gem install bundler -v ${requiredVersion}`, { log: true });
|
|
185
|
+
throw new Error(`Ruby version is out-of-date. Required version is ${requiredVersion}.`);
|
|
220
186
|
}
|
|
221
187
|
}
|
|
222
188
|
|
|
223
|
-
async function
|
|
189
|
+
async function ensureBundle() {
|
|
190
|
+
// Check bundler gem version
|
|
224
191
|
const installedVersion = version.clean(
|
|
225
|
-
(await execute('
|
|
192
|
+
(await execute('bundler -v', { log: false })).match(/(\d+\.\d+\.\d+)/)[0]
|
|
226
193
|
);
|
|
227
|
-
const requiredVersion = version.clean(package.engines.
|
|
194
|
+
const requiredVersion = version.clean(package.engines.bundler);
|
|
228
195
|
const isUpToDate = version.is(installedVersion, '>=', requiredVersion);
|
|
229
196
|
|
|
230
|
-
|
|
231
|
-
logVersionCheck('Ruby', installedVersion, requiredVersion, isUpToDate);
|
|
197
|
+
logVersionCheck('Bundler', installedVersion, requiredVersion, isUpToDate);
|
|
232
198
|
|
|
233
|
-
//
|
|
199
|
+
// Install bundler gem + update Gemfile.lock if needed
|
|
234
200
|
if (!isUpToDate) {
|
|
235
|
-
|
|
201
|
+
logger.log(`Bundler is out-of-date. Installing version ${requiredVersion}...`);
|
|
202
|
+
await execute(`gem install bundler -v ${requiredVersion}`, { log: true });
|
|
203
|
+
await execute(`bundle update --bundler`, { log: true });
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Skip bundle install/update on server
|
|
207
|
+
if (Manager.isServer()) {
|
|
208
|
+
return;
|
|
236
209
|
}
|
|
210
|
+
|
|
211
|
+
// Install and update gems
|
|
212
|
+
logger.log('Running bundle install...');
|
|
213
|
+
await execute('bundle install', { log: true });
|
|
214
|
+
|
|
215
|
+
logger.log('Running bundle update...');
|
|
216
|
+
await execute('bundle update --all', { log: true });
|
|
237
217
|
}
|
|
238
218
|
|
|
239
219
|
async function ensurePeerDependencies() {
|
|
240
220
|
const requiredPeerDependencies = package.peerDependencies || {};
|
|
241
221
|
|
|
242
|
-
// Loop through and make sure project has AT LEAST the required version
|
|
243
222
|
for (let [dependency, ver] of Object.entries(requiredPeerDependencies)) {
|
|
244
223
|
const projectDependencyVersion = version.clean(project?.dependencies?.[dependency] || project?.devDependencies?.[dependency]);
|
|
245
224
|
const location = DEPENDENCY_MAP[dependency] === 'dev' ? '--save-dev' : '';
|
|
246
|
-
const isUpToDate = version.is(projectDependencyVersion, '>=', ver);
|
|
247
225
|
|
|
248
|
-
// Clean version if needed
|
|
249
226
|
ver = version.clean(ver);
|
|
250
227
|
|
|
251
|
-
|
|
252
|
-
// logger.log('Checking peer dep:', dependency, '-->', projectDependencyVersion, '>=', ver);
|
|
228
|
+
const isUpToDate = version.is(projectDependencyVersion, '>=', ver);
|
|
253
229
|
logVersionCheck(dependency, projectDependencyVersion, ver, isUpToDate);
|
|
254
230
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
// Not found
|
|
258
|
-
!projectDependencyVersion
|
|
259
|
-
// Not the right version
|
|
260
|
-
|| !isUpToDate
|
|
261
|
-
) {
|
|
262
|
-
await install(dependency, ver, location);
|
|
231
|
+
if (!projectDependencyVersion || !isUpToDate) {
|
|
232
|
+
await npmInstall(dependency, ver, location);
|
|
263
233
|
}
|
|
264
234
|
}
|
|
265
235
|
}
|
|
266
236
|
|
|
237
|
+
// --- Project setup functions ---
|
|
238
|
+
|
|
267
239
|
function setupScripts() {
|
|
268
|
-
// Setup the scripts
|
|
269
240
|
project.scripts = project.scripts || {};
|
|
270
241
|
|
|
271
|
-
// Setup the scripts
|
|
272
242
|
Object.keys(package.projectScripts).forEach((key) => {
|
|
273
243
|
project.scripts[key] = package.projectScripts[key];
|
|
274
244
|
});
|
|
275
245
|
|
|
276
|
-
// Save the project
|
|
277
246
|
jetpack.write(path.join(process.cwd(), 'package.json'), project);
|
|
278
247
|
}
|
|
279
248
|
|
|
280
249
|
async function ensureCoreFiles() {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
// Log
|
|
284
|
-
logger.log('No src/_config.yml found. Creating default config file...');
|
|
285
|
-
|
|
286
|
-
// Copy default _config.yml
|
|
287
|
-
const sourcePath = path.join(rootPathPackage, 'dist/defaults/src/_config.yml');
|
|
288
|
-
const targetPath = path.join(rootPathProject, 'src/_config.yml');
|
|
289
|
-
|
|
290
|
-
jetpack.copy(sourcePath, targetPath);
|
|
291
|
-
logger.log(`Copied default _config.yml to src/_config.yml`);
|
|
292
|
-
|
|
293
|
-
// Inject new config into config variable
|
|
294
|
-
config = Manager.getConfig('project');
|
|
295
|
-
|
|
296
|
-
// Run gulp defaults task since this is likely the first run
|
|
297
|
-
await execute('UJ_BUILD_MODE=true npm run gulp -- defaults', { log: true });
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
function checkLocality() {
|
|
302
|
-
const installedVersion = project.devDependencies[package.name];
|
|
303
|
-
|
|
304
|
-
// Check if installedVersion is truthy or throw error
|
|
305
|
-
if (!installedVersion) {
|
|
306
|
-
throw new Error(`No installed version of ${package.name} found in devDependencies.`);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// Log if local
|
|
310
|
-
if (installedVersion.startsWith('file:')) {
|
|
311
|
-
logger.warn(`⚠️⚠️⚠️ You are using the local version of ${package.name}. This WILL NOT WORK when published. ⚠️⚠️⚠️`);
|
|
250
|
+
if (jetpack.exists('src/_config.yml')) {
|
|
251
|
+
return;
|
|
312
252
|
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
async function updateBundle() {
|
|
316
|
-
// Log
|
|
317
|
-
logger.log('Running bundle install...');
|
|
318
|
-
await execute('bundle install', { log: true })
|
|
319
|
-
|
|
320
|
-
// Log
|
|
321
|
-
logger.log('Running bundle update...');
|
|
322
|
-
await execute('bundle update --all', { log: true })
|
|
323
|
-
}
|
|
324
253
|
|
|
325
|
-
|
|
326
|
-
// Default to latest
|
|
327
|
-
ver || 'latest';
|
|
254
|
+
logger.log('No src/_config.yml found. Creating default config file...');
|
|
328
255
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
// Build the command
|
|
333
|
-
let command = `npm install ${package}@${ver} ${location || '--save'}`;
|
|
334
|
-
|
|
335
|
-
// Log
|
|
336
|
-
logger.log('Installing:', command);
|
|
256
|
+
const sourcePath = path.join(rootPathPackage, 'dist/defaults/src/_config.yml');
|
|
257
|
+
const targetPath = path.join(rootPathProject, 'src/_config.yml');
|
|
337
258
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
.then(async () => {
|
|
341
|
-
// Read new project
|
|
342
|
-
const projectUpdated = jetpack.read(path.join(process.cwd(), 'package.json'), 'json');
|
|
259
|
+
jetpack.copy(sourcePath, targetPath);
|
|
260
|
+
logger.log(`Copied default _config.yml to src/_config.yml`);
|
|
343
261
|
|
|
344
|
-
|
|
345
|
-
|
|
262
|
+
// Inject new config into config variable
|
|
263
|
+
config = Manager.getConfig('project');
|
|
346
264
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
project.devDependencies = projectUpdated.devDependencies;
|
|
350
|
-
});
|
|
265
|
+
// Run gulp defaults task since this is likely the first run
|
|
266
|
+
await execute('UJ_BUILD_MODE=true npm run gulp -- defaults', { log: true });
|
|
351
267
|
}
|
|
352
268
|
|
|
353
|
-
|
|
354
|
-
async function createCname() {
|
|
355
|
-
// Get the CNAME
|
|
269
|
+
function createCname() {
|
|
356
270
|
const url = config.url || 'https://ultimate-jekyll.itwcreativeworks.com';
|
|
357
|
-
const host = new URL(url).host
|
|
271
|
+
const host = new URL(url).host;
|
|
358
272
|
|
|
359
|
-
// Write to file
|
|
360
273
|
jetpack.write('dist/CNAME', host);
|
|
361
|
-
|
|
362
|
-
// Log
|
|
363
274
|
logger.log('Created CNAME');
|
|
364
275
|
}
|
|
365
276
|
|
|
366
|
-
// Fetch firebase-auth files
|
|
367
277
|
async function fetchFirebaseAuth() {
|
|
368
278
|
const app = config.web_manager.firebase.app.config || {};
|
|
369
|
-
const base = `https://${app.projectId}.firebaseapp.com`;
|
|
370
279
|
|
|
371
|
-
// Throw error if no project ID
|
|
372
280
|
if (!app.projectId) {
|
|
373
|
-
|
|
374
|
-
// return;
|
|
375
|
-
// }
|
|
376
|
-
// throw new Error('No Firebase project ID found in config.web_manager.firebase.app.config.projectId');
|
|
377
|
-
logger.warn('⚠️ Skipping fetchFirebaseAuth due to missing Firebase project ID.');
|
|
281
|
+
logger.warn('Skipping fetchFirebaseAuth due to missing Firebase project ID.');
|
|
378
282
|
return;
|
|
379
283
|
}
|
|
380
284
|
|
|
285
|
+
const base = `https://${app.projectId}.firebaseapp.com`;
|
|
286
|
+
const output = './dist';
|
|
287
|
+
|
|
381
288
|
const files = [
|
|
382
|
-
{
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
},
|
|
386
|
-
{
|
|
387
|
-
remote: '__/auth/handler.js',
|
|
388
|
-
},
|
|
389
|
-
{
|
|
390
|
-
remote: '__/auth/experiments.js',
|
|
391
|
-
},
|
|
289
|
+
{ remote: '__/auth/handler', filename: 'handler.html' },
|
|
290
|
+
{ remote: '__/auth/handler.js' },
|
|
291
|
+
{ remote: '__/auth/experiments.js' },
|
|
392
292
|
{
|
|
393
293
|
remote: '__/auth/iframe',
|
|
394
294
|
filename: 'iframe.html',
|
|
395
|
-
replace: (content) => {
|
|
396
|
-
return content.replace('src="iframe.js"', 'src="iframe.js?cb={{ site.uj.cache_breaker }}"');
|
|
397
|
-
}
|
|
398
|
-
},
|
|
399
|
-
{
|
|
400
|
-
remote: '__/auth/iframe.js',
|
|
295
|
+
replace: (content) => content.replace('src="iframe.js"', 'src="iframe.js?cb={{ site.uj.cache_breaker }}"'),
|
|
401
296
|
},
|
|
402
|
-
{
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
]
|
|
406
|
-
const promises = [];
|
|
407
|
-
const output = './dist';
|
|
297
|
+
{ remote: '__/auth/iframe.js' },
|
|
298
|
+
{ remote: '__/firebase/init.json' },
|
|
299
|
+
];
|
|
408
300
|
|
|
409
|
-
|
|
410
|
-
files.forEach((file) => {
|
|
411
|
-
// Get the remote URL
|
|
412
|
-
const url = `${base}/${file.remote}`;
|
|
301
|
+
logger.log('Fetching firebase-auth files...');
|
|
413
302
|
|
|
414
|
-
|
|
303
|
+
const promises = files.map((file) => {
|
|
304
|
+
const url = `${base}/${file.remote}`;
|
|
415
305
|
const fileName = file.filename
|
|
416
306
|
? path.basename(file.filename)
|
|
417
307
|
: path.basename(file.remote);
|
|
418
308
|
const filePath = path.join(path.dirname(file.remote), fileName);
|
|
419
309
|
const finalPath = path.join(output, filePath);
|
|
420
310
|
|
|
421
|
-
|
|
422
|
-
promises.push(
|
|
423
|
-
fetch(url, {
|
|
424
|
-
response: 'text',
|
|
425
|
-
tries: 3,
|
|
426
|
-
// log: true,
|
|
427
|
-
})
|
|
311
|
+
return fetch(url, { response: 'text', tries: 3 })
|
|
428
312
|
.then((r) => {
|
|
429
|
-
|
|
430
|
-
if (file.replace && typeof file.replace === 'function') {
|
|
313
|
+
if (file.replace) {
|
|
431
314
|
r = file.replace(r);
|
|
432
315
|
}
|
|
433
316
|
|
|
434
|
-
// Log success for this file
|
|
435
317
|
logger.log(`Fetched: ${file.remote}`);
|
|
436
318
|
|
|
437
|
-
// Write to file
|
|
438
319
|
jetpack.write(finalPath,
|
|
439
320
|
'---\n'
|
|
440
321
|
+ `permalink: /${file.remote}\n`
|
|
441
322
|
+ '---\n'
|
|
442
323
|
+ '\n'
|
|
443
324
|
+ r
|
|
444
|
-
)
|
|
325
|
+
);
|
|
445
326
|
})
|
|
446
327
|
.catch((error) => {
|
|
447
|
-
|
|
448
|
-
logger.error(`❌ Failed to fetch: ${file.remote}`);
|
|
328
|
+
logger.error(`Failed to fetch: ${file.remote}`);
|
|
449
329
|
logger.error(` URL: ${url}`);
|
|
450
330
|
|
|
451
|
-
// Check if it's a specific type of error (not HTML response)
|
|
452
|
-
// Use regex to check case-insensitive for HTML content
|
|
453
331
|
const htmlPattern = /<!doctype|<html|<head|<body/i;
|
|
454
332
|
if (error.message && !htmlPattern.test(error.message)) {
|
|
455
333
|
logger.error(` Error: ${error.message}`);
|
|
456
334
|
}
|
|
457
335
|
|
|
458
|
-
// Re-throw to make Promise.all fail
|
|
459
336
|
throw new Error(`Failed to fetch Firebase auth file: ${file.remote}`);
|
|
460
|
-
})
|
|
461
|
-
);
|
|
337
|
+
});
|
|
462
338
|
});
|
|
463
339
|
|
|
464
|
-
// Log
|
|
465
|
-
logger.log('Fetching firebase-auth files...');
|
|
466
|
-
|
|
467
340
|
try {
|
|
468
|
-
// Await all promises
|
|
469
341
|
await Promise.all(promises);
|
|
470
|
-
|
|
471
|
-
// Log success
|
|
472
|
-
logger.log('✅ Fetched firebase-auth files');
|
|
342
|
+
logger.log('Fetched firebase-auth files');
|
|
473
343
|
} catch (error) {
|
|
474
|
-
// Check if we should skip Firebase auth errors
|
|
475
344
|
if (process.env.UJ_SKIP_FIREBASE_AUTH_ERRORS === 'true') {
|
|
476
|
-
logger.warn('
|
|
345
|
+
logger.warn('Failed to fetch some Firebase auth files, but continuing due to UJ_SKIP_FIREBASE_AUTH_ERRORS=true');
|
|
477
346
|
return;
|
|
478
347
|
}
|
|
479
348
|
|
|
480
|
-
// Error already logged above, just throw to stop execution
|
|
481
349
|
throw new Error('Failed to fetch one or more Firebase auth files. Please check your Firebase project configuration.');
|
|
482
350
|
}
|
|
483
351
|
}
|
|
484
352
|
|
|
485
|
-
function
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
353
|
+
function checkLocality() {
|
|
354
|
+
const installedVersion = project.devDependencies[package.name];
|
|
355
|
+
|
|
356
|
+
if (!installedVersion) {
|
|
357
|
+
throw new Error(`No installed version of ${package.name} found in devDependencies.`);
|
|
489
358
|
}
|
|
490
359
|
|
|
491
|
-
|
|
492
|
-
|
|
360
|
+
if (installedVersion.startsWith('file:')) {
|
|
361
|
+
logger.warn(`You are using the local version of ${package.name}. This WILL NOT WORK when published.`);
|
|
362
|
+
}
|
|
493
363
|
}
|
|
494
364
|
|
|
495
|
-
// Publish GH_TOKEN as repository secret
|
|
496
365
|
async function publishGitHubToken() {
|
|
497
|
-
// Check if GH_TOKEN is available
|
|
498
366
|
if (!process.env.GH_TOKEN) {
|
|
499
|
-
logger.warn('
|
|
367
|
+
logger.warn('GH_TOKEN not found in environment variables. Skipping secret publication.');
|
|
500
368
|
return;
|
|
501
369
|
}
|
|
502
370
|
|
|
503
|
-
// Check if GITHUB_REPOSITORY is available
|
|
504
371
|
if (!process.env.GITHUB_REPOSITORY) {
|
|
505
|
-
logger.warn('
|
|
372
|
+
logger.warn('GITHUB_REPOSITORY not detected. Skipping secret publication.');
|
|
506
373
|
return;
|
|
507
374
|
}
|
|
508
375
|
|
|
509
|
-
// Quit if in build mode
|
|
510
376
|
if (Manager.isBuildMode()) {
|
|
511
|
-
logger.log('
|
|
377
|
+
logger.log('Skipping GH_TOKEN publication in build mode.');
|
|
512
378
|
return;
|
|
513
379
|
}
|
|
514
380
|
|
|
515
381
|
try {
|
|
516
|
-
// Parse owner and repo
|
|
517
382
|
const [owner, repo] = process.env.GITHUB_REPOSITORY.split('/');
|
|
518
383
|
|
|
519
|
-
|
|
520
|
-
const octokit = new Octokit({
|
|
521
|
-
auth: process.env.GH_TOKEN,
|
|
522
|
-
});
|
|
384
|
+
const octokit = new Octokit({ auth: process.env.GH_TOKEN });
|
|
523
385
|
|
|
524
|
-
logger.log(
|
|
386
|
+
logger.log(`Publishing GH_TOKEN as repository secret for ${owner}/${repo}...`);
|
|
525
387
|
|
|
526
|
-
// Initialize sodium
|
|
527
388
|
await sodium.ready;
|
|
528
389
|
|
|
529
|
-
|
|
530
|
-
const { data: publicKeyData } = await octokit.actions.getRepoPublicKey({
|
|
531
|
-
owner,
|
|
532
|
-
repo,
|
|
533
|
-
});
|
|
390
|
+
const { data: publicKeyData } = await octokit.actions.getRepoPublicKey({ owner, repo });
|
|
534
391
|
|
|
535
|
-
// Convert secret to Uint8Array
|
|
536
392
|
const secretBytes = Buffer.from(process.env.GH_TOKEN);
|
|
537
393
|
const keyBytes = Buffer.from(publicKeyData.key, 'base64');
|
|
538
|
-
|
|
539
|
-
// Encrypt the secret using libsodium
|
|
540
394
|
const encryptedBytes = sodium.crypto_box_seal(secretBytes, keyBytes);
|
|
541
395
|
const encryptedValue = Buffer.from(encryptedBytes).toString('base64');
|
|
542
396
|
|
|
543
|
-
// Create or update the repository secret
|
|
544
397
|
await octokit.actions.createOrUpdateRepoSecret({
|
|
545
398
|
owner,
|
|
546
399
|
repo,
|
|
@@ -549,139 +402,16 @@ async function publishGitHubToken() {
|
|
|
549
402
|
key_id: publicKeyData.key_id,
|
|
550
403
|
});
|
|
551
404
|
|
|
552
|
-
logger.log(
|
|
553
|
-
} catch (error) {
|
|
554
|
-
logger.error(`❌ Failed to publish GH_TOKEN as repository secret: ${error.message}`);
|
|
555
|
-
// Don't throw - this is not critical for setup to continue
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
// Update GitHub Pages settings
|
|
560
|
-
async function updateGitHubPages(options) {
|
|
561
|
-
options = options || {};
|
|
562
|
-
|
|
563
|
-
// Check if GH_TOKEN is available
|
|
564
|
-
if (!process.env.GH_TOKEN) {
|
|
565
|
-
logger.warn('⚠️ GH_TOKEN not found in environment variables. Skipping GitHub Pages update.');
|
|
566
|
-
return;
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
// Check if GITHUB_REPOSITORY is available
|
|
570
|
-
if (!process.env.GITHUB_REPOSITORY) {
|
|
571
|
-
logger.warn('⚠️ GITHUB_REPOSITORY not detected. Skipping GitHub Pages update.');
|
|
572
|
-
return;
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
// Quit if in build mode
|
|
576
|
-
if (Manager.isBuildMode()) {
|
|
577
|
-
logger.log('⚠️ Skipping GitHub Pages update in build mode.');
|
|
578
|
-
return;
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
try {
|
|
582
|
-
// Parse owner and repo
|
|
583
|
-
const [owner, repo] = process.env.GITHUB_REPOSITORY.split('/');
|
|
584
|
-
|
|
585
|
-
// Initialize Octokit
|
|
586
|
-
const octokit = new Octokit({
|
|
587
|
-
auth: process.env.GH_TOKEN,
|
|
588
|
-
});
|
|
589
|
-
|
|
590
|
-
logger.log(`📄 Configuring GitHub Pages for ${owner}/${repo}...`);
|
|
591
|
-
|
|
592
|
-
// Get current repository info to check if Pages is already enabled
|
|
593
|
-
let pagesInfo;
|
|
594
|
-
try {
|
|
595
|
-
const { data } = await octokit.repos.getPages({
|
|
596
|
-
owner,
|
|
597
|
-
repo,
|
|
598
|
-
});
|
|
599
|
-
pagesInfo = data;
|
|
600
|
-
logger.log('GitHub Pages already enabled, updating configuration...');
|
|
601
|
-
} catch (error) {
|
|
602
|
-
if (error.status === 404) {
|
|
603
|
-
logger.log('GitHub Pages not yet enabled, creating Pages site...');
|
|
604
|
-
pagesInfo = null;
|
|
605
|
-
} else {
|
|
606
|
-
throw error;
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
// Determine the source configuration
|
|
611
|
-
const sourceConfig = options.source || { branch: 'gh-pages', path: '/' };
|
|
612
|
-
|
|
613
|
-
// If custom domain is provided in config, use it
|
|
614
|
-
const customDomain = options.customDomain || (config.url ? new URL(config.url).host : null);
|
|
615
|
-
|
|
616
|
-
// Build options for API call
|
|
617
|
-
const pagesOptions = {
|
|
618
|
-
owner,
|
|
619
|
-
repo,
|
|
620
|
-
source: sourceConfig,
|
|
621
|
-
};
|
|
622
|
-
|
|
623
|
-
// Add custom domain if available and not github.io domain
|
|
624
|
-
if (customDomain && !customDomain.includes('github.io')) {
|
|
625
|
-
pagesOptions.cname = customDomain;
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
// Add HTTPS enforcement (recommended)
|
|
629
|
-
if (options.httpsEnforced !== false) {
|
|
630
|
-
pagesOptions.https_enforced = true;
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
// Create or update Pages configuration
|
|
634
|
-
if (!pagesInfo) {
|
|
635
|
-
// Create new Pages site
|
|
636
|
-
const { data } = await octokit.repos.createPagesSite(pagesOptions);
|
|
637
|
-
logger.log(`✅ GitHub Pages enabled successfully!`);
|
|
638
|
-
logger.log(` URL: ${data.html_url}`);
|
|
639
|
-
if (data.cname) {
|
|
640
|
-
logger.log(` Custom domain: ${data.cname}`);
|
|
641
|
-
}
|
|
642
|
-
} else {
|
|
643
|
-
// Update existing Pages site
|
|
644
|
-
const updateOptions = {
|
|
645
|
-
owner,
|
|
646
|
-
repo,
|
|
647
|
-
};
|
|
648
|
-
|
|
649
|
-
// Only include fields that can be updated
|
|
650
|
-
if (customDomain && !customDomain.includes('github.io')) {
|
|
651
|
-
updateOptions.cname = customDomain;
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
if (options.httpsEnforced !== false) {
|
|
655
|
-
updateOptions.https_enforced = true;
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
// Update source if different
|
|
659
|
-
if (JSON.stringify(pagesInfo.source) !== JSON.stringify(sourceConfig)) {
|
|
660
|
-
updateOptions.source = sourceConfig;
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
const { data } = await octokit.repos.updateInformationAboutPagesSite(updateOptions);
|
|
664
|
-
logger.log(`✅ GitHub Pages configuration updated successfully!`);
|
|
665
|
-
logger.log(` URL: ${data.html_url}`);
|
|
666
|
-
if (data.cname) {
|
|
667
|
-
logger.log(` Custom domain: ${data.cname}`);
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
|
|
405
|
+
logger.log(`Successfully published GH_TOKEN as repository secret`);
|
|
671
406
|
} catch (error) {
|
|
672
|
-
logger.error(
|
|
673
|
-
// Don't throw - this is not critical for setup to continue
|
|
407
|
+
logger.error(`Failed to publish GH_TOKEN as repository secret: ${error.message}`);
|
|
674
408
|
}
|
|
675
409
|
}
|
|
676
410
|
|
|
677
|
-
// Deduplicate posts - removes duplicate posts with same slug but different dates
|
|
678
|
-
// Keeps the ORIGINAL (oldest) post and removes newer duplicates
|
|
679
411
|
async function deduplicatePosts() {
|
|
680
|
-
logger.log('
|
|
412
|
+
logger.log('Checking for duplicate posts...');
|
|
681
413
|
|
|
682
|
-
|
|
683
|
-
const postsGlob = 'src/_posts/**/*.{md,markdown,html}';
|
|
684
|
-
const postFiles = glob(postsGlob, { nodir: true });
|
|
414
|
+
const postFiles = glob('src/_posts/**/*.{md,markdown,html}', { nodir: true });
|
|
685
415
|
|
|
686
416
|
if (postFiles.length === 0) {
|
|
687
417
|
logger.log('No posts found');
|
|
@@ -695,8 +425,6 @@ async function deduplicatePosts() {
|
|
|
695
425
|
|
|
696
426
|
for (const filePath of postFiles) {
|
|
697
427
|
const filename = path.basename(filePath);
|
|
698
|
-
|
|
699
|
-
// Jekyll post format: YYYY-MM-DD-slug.ext
|
|
700
428
|
const match = filename.match(/^(\d{4}-\d{2}-\d{2})-(.+)\.(md|markdown|html)$/);
|
|
701
429
|
|
|
702
430
|
if (!match) {
|
|
@@ -704,20 +432,12 @@ async function deduplicatePosts() {
|
|
|
704
432
|
}
|
|
705
433
|
|
|
706
434
|
const [, dateStr, slug, ext] = match;
|
|
707
|
-
const date = new Date(dateStr);
|
|
708
435
|
|
|
709
436
|
if (!postsBySlug[slug]) {
|
|
710
437
|
postsBySlug[slug] = [];
|
|
711
438
|
}
|
|
712
439
|
|
|
713
|
-
postsBySlug[slug].push({
|
|
714
|
-
filePath,
|
|
715
|
-
filename,
|
|
716
|
-
date,
|
|
717
|
-
dateStr,
|
|
718
|
-
slug,
|
|
719
|
-
ext,
|
|
720
|
-
});
|
|
440
|
+
postsBySlug[slug].push({ filePath, filename, date: new Date(dateStr), dateStr, slug, ext });
|
|
721
441
|
}
|
|
722
442
|
|
|
723
443
|
// Find duplicates and keep only the ORIGINAL (oldest)
|
|
@@ -732,25 +452,22 @@ async function deduplicatePosts() {
|
|
|
732
452
|
// Sort by date ascending (oldest first)
|
|
733
453
|
posts.sort((a, b) => a.date - b.date);
|
|
734
454
|
|
|
735
|
-
// Keep the oldest (original), mark the rest for removal
|
|
736
455
|
const [original, ...newer] = posts;
|
|
737
456
|
|
|
738
457
|
logger.log(`Found ${posts.length} posts with slug "${slug}":`);
|
|
739
|
-
logger.log(`
|
|
458
|
+
logger.log(` Keeping original: ${original.filename} (${original.dateStr})`);
|
|
740
459
|
|
|
741
460
|
for (const post of newer) {
|
|
742
|
-
logger.log(`
|
|
461
|
+
logger.log(` Removing duplicate: ${post.filename} (${post.dateStr})`);
|
|
743
462
|
|
|
744
|
-
// Remove the duplicate post file
|
|
745
463
|
try {
|
|
746
464
|
jetpack.remove(post.filePath);
|
|
747
465
|
removedCount++;
|
|
748
466
|
|
|
749
|
-
// Also remove the associated images folder (src/assets/images/blog/post-SLUG)
|
|
750
467
|
const imageFolder = `src/assets/images/blog/post-${post.slug}`;
|
|
751
468
|
if (jetpack.exists(imageFolder)) {
|
|
752
469
|
jetpack.remove(imageFolder);
|
|
753
|
-
logger.log(`
|
|
470
|
+
logger.log(` Removed image folder: ${imageFolder}`);
|
|
754
471
|
}
|
|
755
472
|
|
|
756
473
|
duplicates.push({
|
|
@@ -765,11 +482,9 @@ async function deduplicatePosts() {
|
|
|
765
482
|
}
|
|
766
483
|
}
|
|
767
484
|
|
|
768
|
-
// Log summary
|
|
769
485
|
if (removedCount > 0) {
|
|
770
|
-
logger.log(
|
|
486
|
+
logger.log(`Removed ${removedCount} duplicate post(s)`);
|
|
771
487
|
|
|
772
|
-
// Save report
|
|
773
488
|
const reportDir = path.join(rootPathProject, '.temp/deduplicate');
|
|
774
489
|
jetpack.dir(reportDir);
|
|
775
490
|
|
|
@@ -784,30 +499,27 @@ async function deduplicatePosts() {
|
|
|
784
499
|
|
|
785
500
|
logger.log(`Report saved to: ${reportPath}`);
|
|
786
501
|
} else {
|
|
787
|
-
logger.log('
|
|
502
|
+
logger.log('No duplicate posts found');
|
|
788
503
|
}
|
|
789
504
|
}
|
|
790
505
|
|
|
791
|
-
//
|
|
506
|
+
// --- Migration functions ---
|
|
507
|
+
|
|
792
508
|
async function migrate() {
|
|
793
509
|
const installedVersion = project.devDependencies[package.name] || '0.0.0';
|
|
794
510
|
|
|
795
|
-
// Skip if using local version
|
|
796
511
|
if (installedVersion.startsWith('file:')) {
|
|
797
512
|
return;
|
|
798
513
|
}
|
|
799
514
|
|
|
800
|
-
// Migrate hooks to nested structure (introduced in 0.0.185)
|
|
801
515
|
if (version.is(installedVersion, '<=', '1.0.0')) {
|
|
802
516
|
await migrateHooksToNestedStructure();
|
|
803
517
|
}
|
|
804
518
|
}
|
|
805
519
|
|
|
806
|
-
// Migrate old hook files to new nested structure
|
|
807
520
|
async function migrateHooksToNestedStructure() {
|
|
808
521
|
const hooksDir = path.join(rootPathProject, 'hooks');
|
|
809
522
|
|
|
810
|
-
// Map of old file names to new paths
|
|
811
523
|
const migrations = [
|
|
812
524
|
{ old: 'build:post.js', new: 'build/post.js' },
|
|
813
525
|
{ old: 'build:pre.js', new: 'build/pre.js' },
|
|
@@ -820,23 +532,49 @@ async function migrateHooksToNestedStructure() {
|
|
|
820
532
|
const oldPath = path.join(hooksDir, migration.old);
|
|
821
533
|
const newPath = path.join(hooksDir, migration.new);
|
|
822
534
|
|
|
823
|
-
// Check if old file exists
|
|
824
535
|
if (!jetpack.exists(oldPath)) {
|
|
825
536
|
continue;
|
|
826
537
|
}
|
|
827
538
|
|
|
828
|
-
// Check if new file already exists
|
|
829
539
|
if (jetpack.exists(newPath)) {
|
|
830
|
-
logger.warn(
|
|
540
|
+
logger.warn(`Migrate ${migration.old}: ${migration.new} already exists`);
|
|
831
541
|
}
|
|
832
542
|
|
|
833
|
-
// Move the file
|
|
834
543
|
jetpack.move(oldPath, newPath, { overwrite: true });
|
|
835
|
-
logger.log(
|
|
544
|
+
logger.log(`Migrated hook: ${migration.old} -> ${migration.new}`);
|
|
836
545
|
migratedCount++;
|
|
837
546
|
}
|
|
838
547
|
|
|
839
548
|
if (migratedCount > 0) {
|
|
840
|
-
logger.log(
|
|
549
|
+
logger.log(`Migrated ${migratedCount} hook file(s) to new nested structure`);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// --- Utility functions ---
|
|
554
|
+
|
|
555
|
+
function npmInstall(pkg, ver, location) {
|
|
556
|
+
ver = ver || 'latest';
|
|
557
|
+
ver = ver === 'latest' ? ver : version.clean(ver);
|
|
558
|
+
|
|
559
|
+
const command = `npm install ${pkg}@${ver} ${location || '--save'}`;
|
|
560
|
+
|
|
561
|
+
logger.log('Installing:', command);
|
|
562
|
+
|
|
563
|
+
return execute(command, { log: true })
|
|
564
|
+
.then(() => {
|
|
565
|
+
const projectUpdated = jetpack.read(path.join(process.cwd(), 'package.json'), 'json');
|
|
566
|
+
|
|
567
|
+
logger.log('Installed:', pkg, ver);
|
|
568
|
+
|
|
569
|
+
project.dependencies = projectUpdated.dependencies;
|
|
570
|
+
project.devDependencies = projectUpdated.devDependencies;
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
function logVersionCheck(name, installedVersion, latestVersion, isUpToDate) {
|
|
575
|
+
if (installedVersion.startsWith('file:')) {
|
|
576
|
+
isUpToDate = true;
|
|
841
577
|
}
|
|
578
|
+
|
|
579
|
+
logger.log(`Checking if ${name} is up to date (${logger.format.bold(installedVersion)} >= ${logger.format.bold(latestVersion)}): ${isUpToDate ? logger.format.green('Yes') : logger.format.red('No')}`);
|
|
842
580
|
}
|
|
@@ -85,3 +85,25 @@ exclude:
|
|
|
85
85
|
include:
|
|
86
86
|
- .well-known
|
|
87
87
|
- __
|
|
88
|
+
|
|
89
|
+
# Jekyll collections
|
|
90
|
+
collections:
|
|
91
|
+
team:
|
|
92
|
+
title: "Team Members"
|
|
93
|
+
output: true
|
|
94
|
+
updates:
|
|
95
|
+
title: "Updates"
|
|
96
|
+
output: true
|
|
97
|
+
|
|
98
|
+
# Jekyll defaults
|
|
99
|
+
defaults:
|
|
100
|
+
- scope:
|
|
101
|
+
path: ""
|
|
102
|
+
type: "team"
|
|
103
|
+
values:
|
|
104
|
+
permalink: "/team/:title"
|
|
105
|
+
- scope:
|
|
106
|
+
path: ""
|
|
107
|
+
type: "updates"
|
|
108
|
+
values:
|
|
109
|
+
permalink: "/updates/:title"
|
|
@@ -243,25 +243,3 @@ pagination:
|
|
|
243
243
|
title: ":title - page :num"
|
|
244
244
|
sort_field: "date"
|
|
245
245
|
sort_reverse: true
|
|
246
|
-
|
|
247
|
-
# Jekyll collections
|
|
248
|
-
collections:
|
|
249
|
-
team:
|
|
250
|
-
title: "Team Members"
|
|
251
|
-
output: true
|
|
252
|
-
updates:
|
|
253
|
-
title: "Updates"
|
|
254
|
-
output: true
|
|
255
|
-
|
|
256
|
-
# Jekyll defaults
|
|
257
|
-
defaults:
|
|
258
|
-
- scope:
|
|
259
|
-
path: ""
|
|
260
|
-
type: "team"
|
|
261
|
-
values:
|
|
262
|
-
permalink: "/team/:title"
|
|
263
|
-
- scope:
|
|
264
|
-
path: ""
|
|
265
|
-
type: "updates"
|
|
266
|
-
values:
|
|
267
|
-
permalink: "/updates/:title"
|
|
@@ -7,6 +7,7 @@ const glob = require('glob').globSync;
|
|
|
7
7
|
const path = require('path');
|
|
8
8
|
const { execute, template } = require('node-powertools');
|
|
9
9
|
const jetpack = require('fs-jetpack');
|
|
10
|
+
const mergeJekyllConfigs = require('./utils/merge-jekyll-configs');
|
|
10
11
|
|
|
11
12
|
// Templates
|
|
12
13
|
const JSONP_TEMPLATE = `
|
|
@@ -79,6 +80,14 @@ async function jekyll(complete) {
|
|
|
79
80
|
// Run buildpre hook
|
|
80
81
|
await hook('build/pre', index);
|
|
81
82
|
|
|
83
|
+
// Merge UJM collections/defaults with project collections/defaults
|
|
84
|
+
const mergedConfigPath = mergeJekyllConfigs(
|
|
85
|
+
`./node_modules/${package.name}/dist/config/_config_default.yml`,
|
|
86
|
+
'dist/_config.yml',
|
|
87
|
+
'.temp/_config_collections.yml',
|
|
88
|
+
logger,
|
|
89
|
+
);
|
|
90
|
+
|
|
82
91
|
// Build Jekyll
|
|
83
92
|
const command = [
|
|
84
93
|
// Enable Ruby YJIT for faster builds
|
|
@@ -95,13 +104,15 @@ async function jekyll(complete) {
|
|
|
95
104
|
`./node_modules/${package.name}/dist/config/_config_default.yml`,
|
|
96
105
|
// This is the user's project config file
|
|
97
106
|
'dist/_config.yml',
|
|
107
|
+
// Merged collections/defaults (deep merge of UJM + project)
|
|
108
|
+
mergedConfigPath || '',
|
|
98
109
|
// Add browsersync config IF BUILD_MODE is not true
|
|
99
110
|
Manager.isBuildMode() ? '' : '.temp/_config_browsersync.yml',
|
|
100
111
|
// Add development config IF BUILD_MODE is not true
|
|
101
112
|
Manager.isBuildMode() ? '' : `./node_modules/${package.name}/dist/config/_config_development.yml`,
|
|
102
113
|
// Add project-level dev config IF it exists and BUILD_MODE is not true
|
|
103
114
|
(!Manager.isBuildMode() && jetpack.exists('dist/_config.dev.yml')) ? 'dist/_config.dev.yml' : '',
|
|
104
|
-
].join(','),
|
|
115
|
+
].filter(Boolean).join(','),
|
|
105
116
|
'--incremental',
|
|
106
117
|
(Manager.isBuildMode() || argv.profile) ? ' --profile' : '',
|
|
107
118
|
// Limit posts in development for faster builds (use --all-posts to disable)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// Libraries
|
|
2
|
+
const jetpack = require('fs-jetpack');
|
|
3
|
+
const yaml = require('js-yaml');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Merges Jekyll collections and defaults from UJM's config with the
|
|
7
|
+
* consuming project's config. Jekyll's --config does a shallow merge
|
|
8
|
+
* of top-level keys, so a project defining its own `collections` would
|
|
9
|
+
* completely override UJM's. This performs a deep merge so both coexist.
|
|
10
|
+
*/
|
|
11
|
+
function mergeJekyllConfigs(ujmConfigPath, projectConfigPath, outputPath, logger) {
|
|
12
|
+
// Read configs
|
|
13
|
+
const ujmContent = jetpack.read(ujmConfigPath);
|
|
14
|
+
if (!ujmContent) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const ujmConfig = yaml.load(ujmContent) || {};
|
|
19
|
+
const projectContent = jetpack.read(projectConfigPath);
|
|
20
|
+
const projectConfig = projectContent ? (yaml.load(projectContent) || {}) : {};
|
|
21
|
+
|
|
22
|
+
// Extract collections and defaults from both
|
|
23
|
+
const ujmCollections = ujmConfig.collections || {};
|
|
24
|
+
const ujmDefaults = ujmConfig.defaults || [];
|
|
25
|
+
const projectCollections = projectConfig.collections || {};
|
|
26
|
+
const projectDefaults = projectConfig.defaults || [];
|
|
27
|
+
|
|
28
|
+
// Nothing to merge
|
|
29
|
+
if (!Object.keys(ujmCollections).length && !ujmDefaults.length) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const merged = {};
|
|
34
|
+
|
|
35
|
+
// Merge collections (object spread: UJM base + project overrides/additions)
|
|
36
|
+
if (Object.keys(ujmCollections).length || Object.keys(projectCollections).length) {
|
|
37
|
+
merged.collections = { ...ujmCollections, ...projectCollections };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Merge defaults (concat + dedup by scope key, project wins on conflict)
|
|
41
|
+
if (ujmDefaults.length || projectDefaults.length) {
|
|
42
|
+
const defaultsMap = new Map();
|
|
43
|
+
|
|
44
|
+
// UJM defaults first (base)
|
|
45
|
+
for (const entry of ujmDefaults) {
|
|
46
|
+
defaultsMap.set(getDefaultsKey(entry), entry);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Project defaults override for same scope
|
|
50
|
+
for (const entry of projectDefaults) {
|
|
51
|
+
defaultsMap.set(getDefaultsKey(entry), entry);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
merged.defaults = Array.from(defaultsMap.values());
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Write merged config
|
|
58
|
+
const content = `# Auto-generated merged config for collections and defaults\n# DO NOT EDIT - this file is regenerated on every build\n\n${yaml.dump(merged, { lineWidth: -1, noRefs: true })}`;
|
|
59
|
+
|
|
60
|
+
jetpack.write(outputPath, content);
|
|
61
|
+
logger.log(`Merged collections config written to ${outputPath}`);
|
|
62
|
+
|
|
63
|
+
return outputPath;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function getDefaultsKey(entry) {
|
|
67
|
+
const scope = entry.scope || {};
|
|
68
|
+
return `${scope.path || ''}::${scope.type || ''}`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
module.exports = mergeJekyllConfigs;
|