@epublishing/grunt-epublishing 1.2.3 → 1.2.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.
@@ -296,6 +296,129 @@ async function processMinify(config, options = {}) {
296
296
  return results;
297
297
  }
298
298
 
299
+ /**
300
+ * Concatenate application.js from the jade hierarchy (jade -> jade_* -> site)
301
+ * into <siteRoot>/public/javascripts/application.js.
302
+ *
303
+ * The jade helper `javascript_include_defaults` emits
304
+ * <script src="/javascripts/application.js" defer>
305
+ * whenever `Jade[ 'public/javascripts/application.js' ]` resolves. In the
306
+ * legacy Grunt world, this file was served directly from an engine's
307
+ * public dir through the StaticJade* middleware chain; that path still
308
+ * exists in production but depends on every engine's middleware firing
309
+ * in the right order, which has been fragile under Rails 8 / Propshaft.
310
+ *
311
+ * The simple, deterministic fix is to produce a real file at the site's
312
+ * own public path during build — concatenating any application.js files
313
+ * found in the jade hierarchy so inheritance is preserved. If no sources
314
+ * exist, the step is a no-op. If the site has its own application.js at
315
+ * the destination path, we don't self-concat into oblivion.
316
+ *
317
+ * This runs as a built-in step of `runConcatMinify`; sites do not need to
318
+ * declare an `application` concat target in build.config.js for it to
319
+ * happen. If a user-defined `concat.application` target IS present, that
320
+ * wins (the built-in step bows out).
321
+ *
322
+ * @param {Object} config - Full build configuration
323
+ * @param {Object} options - Build options
324
+ * @returns {Promise<Object|null>} Result object or null if skipped
325
+ */
326
+ async function concatApplicationJs(config, options = {}) {
327
+ const { verbose = false } = options;
328
+ const paths = config.paths || {};
329
+ const siteRoot = paths.site || process.cwd();
330
+ const jsDir = paths.js || 'public/javascripts';
331
+
332
+ // If the user already declared `concat.application`, they own the
333
+ // target — don't stomp it.
334
+ if (config.concat && config.concat.application) {
335
+ if (verbose) {
336
+ console.log(' Skipping built-in application.js bundler: user defined concat.application');
337
+ }
338
+ return { target: 'application', success: true, skipped: true, reason: 'user-defined' };
339
+ }
340
+
341
+ const destPath = path.resolve(siteRoot, jsDir, 'application.js');
342
+
343
+ // Walk the jade hierarchy in inheritance order: jade engine first,
344
+ // then any jade_* child gem (jade_bnp, jade_sosland, etc.), then the
345
+ // site's own file. Later files override earlier behavior via the
346
+ // natural order of script evaluation.
347
+ const candidateSources = [];
348
+
349
+ if (paths.jade) {
350
+ candidateSources.push(path.join(paths.jade, jsDir, 'application.js'));
351
+ }
352
+
353
+ for (const key of Object.keys(paths)) {
354
+ // `jadechild` is an alias for the primary child gem and duplicates
355
+ // one of the `jade_*` entries; skip it to avoid double-including.
356
+ if (key === 'jadechild') continue;
357
+ if (!key.startsWith('jade_')) continue;
358
+ const childPath = paths[key];
359
+ if (!childPath || typeof childPath !== 'string') continue;
360
+ candidateSources.push(path.join(childPath, jsDir, 'application.js'));
361
+ }
362
+
363
+ // Site's own application.js (typically the destination path itself if
364
+ // it exists from a previous build; real site sources would live at
365
+ // app/js/application.js or similar and would already be handled by
366
+ // user-defined concat targets, so we only read the destination).
367
+ const siteSource = path.resolve(siteRoot, jsDir, 'application.js');
368
+
369
+ // De-dupe (jadechild alias could sneak through different keys) and
370
+ // keep only existing files.
371
+ const seen = new Set();
372
+ const sources = candidateSources
373
+ .filter(src => {
374
+ if (seen.has(src)) return false;
375
+ seen.add(src);
376
+ return true;
377
+ })
378
+ .filter(src => fs.existsSync(src));
379
+
380
+ // Include the site's own application.js if it's a real source file
381
+ // (i.e., not just the destination we're about to overwrite).
382
+ if (fs.existsSync(siteSource) && siteSource !== destPath) {
383
+ sources.push(siteSource);
384
+ }
385
+
386
+ if (sources.length === 0) {
387
+ if (verbose) {
388
+ console.log(' No application.js sources found in jade hierarchy — skipping');
389
+ }
390
+ return { target: 'application', success: true, skipped: true, reason: 'no-sources' };
391
+ }
392
+
393
+ try {
394
+ const content = await concatFiles(sources, { separator: ';\n', stripBanners: false });
395
+ await fs.promises.mkdir(path.dirname(destPath), { recursive: true });
396
+ await fs.promises.writeFile(destPath, content);
397
+
398
+ if (verbose) {
399
+ console.log(` Bundled application.js from ${sources.length} source(s):`);
400
+ for (const src of sources) {
401
+ console.log(` ${src}`);
402
+ }
403
+ console.log(` -> ${destPath}`);
404
+ }
405
+
406
+ return {
407
+ target: 'application',
408
+ dest: destPath,
409
+ sources,
410
+ success: true,
411
+ };
412
+ } catch (error) {
413
+ return {
414
+ target: 'application',
415
+ dest: destPath,
416
+ success: false,
417
+ error,
418
+ };
419
+ }
420
+ }
421
+
299
422
  /**
300
423
  * Run full concat + minify pipeline
301
424
  * @param {Object} config - Full build configuration
@@ -310,6 +433,15 @@ async function runConcatMinify(config, options = {}) {
310
433
  }
311
434
  const concatResults = await processConcat(config, options);
312
435
 
436
+ // Built-in bundler for the legacy jade `application.js` file.
437
+ if (verbose) {
438
+ console.log('Bundling application.js from jade hierarchy...');
439
+ }
440
+ const applicationResult = await concatApplicationJs(config, options);
441
+ if (applicationResult && !applicationResult.skipped) {
442
+ concatResults.push(applicationResult);
443
+ }
444
+
313
445
  if (verbose) {
314
446
  console.log('Running minify...');
315
447
  }
@@ -327,6 +459,7 @@ module.exports = {
327
459
  generateBanner,
328
460
  processConcat,
329
461
  processMinify,
462
+ concatApplicationJs,
330
463
  runConcatMinify,
331
464
  };
332
465
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@epublishing/grunt-epublishing",
3
3
  "description": "Modern front-end build tools for ePublishing Jade and client sites.",
4
- "version": "1.2.3",
4
+ "version": "1.2.4",
5
5
  "homepage": "https://www.epublishing.com",
6
6
  "contributors": [
7
7
  {
@@ -58,6 +58,7 @@
58
58
  "prettyjson": "^1.2.5",
59
59
  "read-pkg": "^5.2.0",
60
60
  "sass-embedded": "^1.80.0",
61
+ "sass-embedded-linux-x64": "^1.80.0",
61
62
  "style-loader": "^4.0.0",
62
63
  "susy": "^2.2.14",
63
64
  "swc-loader": "^0.2.6",
@@ -73,7 +74,6 @@
73
74
  "@swc/core-linux-arm64-gnu": "^1.7.0",
74
75
  "sass-embedded-darwin-arm64": "^1.80.0",
75
76
  "sass-embedded-darwin-x64": "^1.80.0",
76
- "sass-embedded-linux-x64": "^1.80.0",
77
77
  "sass-embedded-linux-arm64": "^1.80.0"
78
78
  },
79
79
  "keywords": [