@m2c2kit/build-helpers 0.3.5 → 0.3.7

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/index.d.ts CHANGED
@@ -29,9 +29,10 @@ import { InputOptions } from 'rollup';
29
29
  *
30
30
  * @param rootDir - root directory of build, usually "dist" because you
31
31
  * usually hash only production builds
32
+ * @param cwd - current working directory; used only for testing.
32
33
  * @returns
33
34
  */
34
- declare function hashM2c2kitAssets(rootDir: string): {
35
+ declare function hashM2c2kitAssets(rootDir: string, cwd?: string): {
35
36
  name: string;
36
37
  closeBundle: {
37
38
  sequential: boolean;
package/dist/index.js CHANGED
@@ -1,38 +1,9 @@
1
- import { dirname as dirname_m2c2kit_build_helpers } from 'path';
2
- import { fileURLToPath as fileURLToPath_m2c2kit_build_helpers} from 'url';
3
- const __filename = fileURLToPath_m2c2kit_build_helpers(import.meta.url);
4
- const __dirname = dirname_m2c2kit_build_helpers(__filename);
5
1
  import { existsSync, renameSync, readFileSync } from 'fs';
6
2
  import { readFile, writeFile } from 'fs/promises';
7
3
  import path from 'path';
8
4
  import { createHash } from 'crypto';
9
5
  import { fileURLToPath } from 'url';
10
6
 
11
- /******************************************************************************
12
- Copyright (c) Microsoft Corporation.
13
-
14
- Permission to use, copy, modify, and/or distribute this software for any
15
- purpose with or without fee is hereby granted.
16
-
17
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
18
- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
19
- AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
20
- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
21
- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
22
- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
23
- PERFORMANCE OF THIS SOFTWARE.
24
- ***************************************************************************** */
25
-
26
- function __awaiter(thisArg, _arguments, P, generator) {
27
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
28
- return new (P || (P = Promise))(function (resolve, reject) {
29
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
30
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
31
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
32
- step((generator = generator.apply(thisArg, _arguments || [])).next());
33
- });
34
- }
35
-
36
7
  // Generated using scripts/write-decode-map.ts
37
8
  var htmlDecodeTree = new Uint16Array(
38
9
  // prettier-ignore
@@ -11158,10 +11129,10 @@ if (!String.prototype.endsWith) {
11158
11129
  }
11159
11130
 
11160
11131
  const OPERATOR_PRECEDENCE = {
11161
- '||': 3,
11132
+ '||': 2,
11133
+ '??': 3,
11162
11134
  '&&': 4,
11163
11135
  '|': 5,
11164
- '??': 5,
11165
11136
  '^': 6,
11166
11137
  '&': 7,
11167
11138
  '==': 8,
@@ -11263,6 +11234,14 @@ function expressionNeedsParenthesis(state, node, parentNode, isRightHand) {
11263
11234
  // Exponentiation operator has right-to-left associativity
11264
11235
  return !isRightHand
11265
11236
  }
11237
+ if (
11238
+ nodePrecedence === 13 &&
11239
+ parentNodePrecedence === 13 &&
11240
+ (node.operator === '??' || parentNode.operator === '??')
11241
+ ) {
11242
+ // Nullish coalescing and boolean operators cannot be combined
11243
+ return true
11244
+ }
11266
11245
  if (isRightHand) {
11267
11246
  // Parenthesis are used if both operators have the same precedence
11268
11247
  return (
@@ -12305,349 +12284,358 @@ function generate(node, options) {
12305
12284
  return state.output
12306
12285
  }
12307
12286
 
12308
- /** We use MD5 for hashing, but keep only the first 16 characters. */
12309
12287
  const HASH_CHARACTER_LENGTH = 16;
12310
- /**
12311
- * rollup plugin to hash m2c2kit assets and enable cache busting.
12312
- *
12313
- * @remarks On repeated deployments, a file with the same name may be
12314
- * cached by browsers. Thus, the old file will be used, not the updated
12315
- * one. To enable cache busting, this rollup plugin adds hashes to filenames
12316
- * for m2c2kit assets: index.js, canvaskit.wasm, images, and fonts. This is
12317
- * done by: 1) parsing index.html and finding the links to css and index.js,
12318
- * calculating a hash of these assets, adding a hash to the asset filenames,
12319
- * and updating the links in index.html to reflect the hash. 2) parsing
12320
- * index.js into an AST, finding string literals that are the urls of fonts,
12321
- * images, and canvaskit.wam, calculating a hash of these assets, adding a
12322
- * hash to the asset filenames, and updating the urls to reflect the hash.
12323
- *
12324
- * Note: index.js is transpiled from TypeScript and no longer has types, so
12325
- * finding the asset urls in the AST assumes that there will be no
12326
- * user code with the same structure as the asset urls. In other words:
12327
- * Don't use a define a property named "canvasKitWasmUrl" that refers to
12328
- * a string literal, don't define a property named "fonts" that that refers
12329
- * to an array expression of object expressions with properties named
12330
- * "fontName" and "url", and don't define a property called "images" that
12331
- * refers to an array expression of object expressions with properties named
12332
- * "imageName", "height", "width", and "url". Otherwise, this plugin may alter
12333
- * your code in unexpected ways (although most likely it will simply give
12334
- * warnings, because it's unlikely there will be valid file assets that will
12335
- * be found and hashed).
12336
- *
12337
- * @param rootDir - root directory of build, usually "dist" because you
12338
- * usually hash only production builds
12339
- * @returns
12340
- */
12341
- function hashM2c2kitAssets(rootDir) {
12342
- const indexHtmlFile = path.join(rootDir, "index.html");
12343
- const indexJsFile = path.join(rootDir, "index.js");
12344
- return {
12345
- name: "hash-m2c2kit-assets",
12346
- closeBundle: {
12347
- sequential: true,
12348
- handler() {
12349
- return __awaiter(this, void 0, void 0, function* () {
12350
- const fileRenames = new Array();
12351
- // Parse index.js using acorn and acorn-walk
12352
- let indexjs;
12353
- try {
12354
- indexjs = yield readFile(indexJsFile, "utf-8");
12355
- }
12356
- catch (_a) {
12357
- throw new Error("could not hash m2c2 assets because there was an error reading index.js. This is a fatal problem because index.js is required.");
12358
- }
12359
- let ast;
12360
- try {
12361
- ast = parse(indexjs, { ecmaVersion: 2020 });
12362
- }
12363
- catch (_b) {
12364
- throw new Error("could not hash m2c2 assets because there was an error parsing index.js. This is a fatal problem because index.js is not valid JavaScript.");
12365
- }
12366
- // it was helpful to use https://astexplorer.net/ to understand the AST
12367
- ancestor(ast, {
12368
- // this code will be run each time the walker visits a literal
12369
- Literal(node, ancestors) {
12370
- if (ancestors.length >= 2) {
12371
- const maybeProperty = ancestors.slice(-2)[0];
12372
- if (maybeProperty.type === "Property") {
12373
- const property = maybeProperty;
12374
- if (property.key.type === "Identifier" &&
12375
- property.key.name == "canvasKitWasmUrl") {
12376
- // property is canvasKitWasmUrl
12377
- const literal = node;
12378
- const originalUrlValue = literal.value;
12379
- try {
12380
- const hashedUrlValue = addHashToUrl(originalUrlValue,
12381
- /**
12382
- * by our convention, the wasm file will be served from
12383
- * the assets directory, so the location is
12384
- * `assets/${canvasKitWasmUrl}` not `${canvasKitWasmUrl}`
12385
- */
12386
- `${rootDir}/assets`);
12387
- literal.value = literal.value.replace(originalUrlValue, hashedUrlValue);
12388
- literal.raw = literal.raw.replace(originalUrlValue, hashedUrlValue);
12389
- addFileToFilesToBeRenamed(`${rootDir}/assets`, originalUrlValue, hashedUrlValue, fileRenames);
12390
- }
12391
- catch (_a) {
12392
- console.log(`warning: could not hash canvaskit.wasm resource because it was not found at ${originalUrlValue}`);
12393
- }
12394
- }
12395
- }
12396
- }
12397
- // we'll be looking back 5 levels
12398
- if (ancestors.length >= 5) {
12399
- const maybeProperty = ancestors.slice(-2)[0];
12400
- if (maybeProperty.type === "Property") {
12401
- const property = maybeProperty;
12402
- if (property.key.type === "Identifier" &&
12403
- property.key.name == "url") {
12404
- // property is url
12405
- const maybeObjExpression = ancestors.slice(-3)[0];
12406
- if (maybeObjExpression.type === "ObjectExpression") {
12407
- const objExpression = maybeObjExpression;
12408
- const properties = objExpression.properties
12409
- .filter((p) => p.type === "Property")
12410
- .map((p) => p);
12411
- const identifiers = properties
12412
- .filter((p) => p.key.type === "Identifier")
12413
- .map((p) => p.key.name);
12414
- const urlFontAssetProperties = ["fontName", "url"];
12415
- const propCount = identifiers.filter((i) => urlFontAssetProperties.indexOf(i) !== -1).length;
12416
- if (propCount === 2) {
12417
- // the object expression has the 2 properties
12418
- const maybeArrayExpression = ancestors.slice(-4)[0];
12419
- if (maybeArrayExpression.type === "ArrayExpression") {
12420
- const maybeProperty = ancestors.slice(-5)[0];
12421
- if (maybeProperty.type === "Property") {
12422
- const property = maybeProperty;
12423
- if (property.key.type === "Identifier" &&
12424
- property.key.name == "fonts") {
12425
- // property is fonts
12426
- const literal = node;
12427
- const originalUrlValue = literal.value;
12428
- try {
12429
- const hashedUrlValue = addHashToUrl(originalUrlValue, rootDir);
12430
- literal.value = literal.value.replace(originalUrlValue, hashedUrlValue);
12431
- literal.raw = literal.raw.replace(originalUrlValue, hashedUrlValue);
12432
- addFileToFilesToBeRenamed(rootDir, originalUrlValue, hashedUrlValue, fileRenames);
12433
- }
12434
- catch (_b) {
12435
- }
12436
- }
12437
- }
12438
- }
12439
- }
12440
- }
12441
- }
12442
- }
12443
- }
12444
- // we'll be looking back 5 levels
12445
- if (ancestors.length >= 5) {
12446
- const maybeProperty = ancestors.slice(-2)[0];
12447
- if (maybeProperty.type === "Property") {
12448
- const property = maybeProperty;
12449
- if (property.key.type === "Identifier" &&
12450
- property.key.name == "url") {
12451
- // property is url
12452
- const maybeObjExpression = ancestors.slice(-3)[0];
12453
- if (maybeObjExpression.type === "ObjectExpression") {
12454
- const objExpression = maybeObjExpression;
12455
- const properties = objExpression.properties
12456
- .filter((p) => p.type === "Property")
12457
- .map((p) => p);
12458
- const identifiers = properties
12459
- .filter((p) => p.key.type === "Identifier")
12460
- .map((p) => p.key.name);
12461
- const urlBrowserImageProperties = [
12462
- "imageName",
12463
- "height",
12464
- "width",
12465
- "url",
12466
- ];
12467
- const propCount = identifiers.filter((i) => urlBrowserImageProperties.indexOf(i) !== -1).length;
12468
- if (propCount === 4) {
12469
- // the object expression has the 4 properties
12470
- const maybeArrayExpression = ancestors.slice(-4)[0];
12471
- if (maybeArrayExpression.type === "ArrayExpression") {
12472
- const maybeProperty = ancestors.slice(-5)[0];
12473
- if (maybeProperty.type === "Property") {
12474
- const property = maybeProperty;
12475
- if (property.key.type === "Identifier" &&
12476
- property.key.name == "images") {
12477
- // property is images
12478
- const literal = node;
12479
- const originalUrlValue = literal.value;
12480
- try {
12481
- const hashedUrlValue = addHashToUrl(originalUrlValue, rootDir);
12482
- literal.value = literal.value.replace(originalUrlValue, hashedUrlValue);
12483
- literal.raw = literal.raw.replace(originalUrlValue, hashedUrlValue);
12484
- addFileToFilesToBeRenamed(rootDir, originalUrlValue, hashedUrlValue, fileRenames);
12485
- }
12486
- catch (_c) {
12487
- }
12488
- }
12489
- }
12490
- }
12491
- }
12492
- }
12493
- }
12494
- }
12495
- }
12496
- },
12497
- });
12498
- const indexJsCode = generate(ast);
12499
- yield writeFile(indexJsFile, indexJsCode);
12500
- // Parse links in index.html using htmlparser2
12501
- // note: if link has attribute hash="false", then hashing
12502
- // is skipped for that link
12503
- const rawHtml = yield readFile(indexHtmlFile, "utf-8");
12504
- const dom = parseDocument(rawHtml);
12505
- // handle the css links
12506
- const links = selectAll("link", dom);
12507
- links
12508
- .map((link) => link)
12509
- .forEach((link) => {
12510
- var _a;
12511
- if (link.attribs["href"].endsWith(".css") &&
12512
- !(((_a = link.attribs["hash"]) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === "false")) {
12513
- const originalUrl = link.attribs["href"];
12288
+ function hashM2c2kitAssets(rootDir, cwd = "") {
12289
+ const indexHtmlFile = path.join(cwd, rootDir, "index.html");
12290
+ const indexJsFile = path.join(cwd, rootDir, "index.js");
12291
+ return {
12292
+ name: "hash-m2c2kit-assets",
12293
+ closeBundle: {
12294
+ sequential: true,
12295
+ async handler() {
12296
+ const fileRenames = new Array();
12297
+ let indexjs;
12298
+ try {
12299
+ indexjs = await readFile(indexJsFile, "utf-8");
12300
+ } catch {
12301
+ throw new Error(
12302
+ "could not hash m2c2 assets because there was an error reading index.js. This is a fatal problem because index.js is required."
12303
+ );
12304
+ }
12305
+ let ast;
12306
+ try {
12307
+ ast = parse(indexjs, { ecmaVersion: 2020 });
12308
+ } catch {
12309
+ throw new Error(
12310
+ "could not hash m2c2 assets because there was an error parsing index.js. This is a fatal problem because index.js is not valid JavaScript."
12311
+ );
12312
+ }
12313
+ ancestor(ast, {
12314
+ // this code will be run each time the walker visits a literal
12315
+ Literal(node, ancestors) {
12316
+ if (ancestors.length >= 2) {
12317
+ const maybeProperty = ancestors.slice(-2)[0];
12318
+ if (maybeProperty.type === "Property") {
12319
+ const property = maybeProperty;
12320
+ if (property.key.type === "Identifier" && property.key.name == "canvasKitWasmUrl") {
12321
+ const literal = node;
12322
+ const originalUrlValue = literal.value;
12323
+ try {
12324
+ const hashedUrlValue = addHashToUrl(
12325
+ originalUrlValue,
12326
+ /**
12327
+ * by our convention, the wasm file will be served from
12328
+ * the assets directory, so the location is
12329
+ * `assets/${canvasKitWasmUrl}` not `${canvasKitWasmUrl}`
12330
+ */
12331
+ `${rootDir}/assets`,
12332
+ cwd
12333
+ );
12334
+ literal.value = literal.value.replace(
12335
+ originalUrlValue,
12336
+ hashedUrlValue
12337
+ );
12338
+ literal.raw = literal.raw.replace(
12339
+ originalUrlValue,
12340
+ hashedUrlValue
12341
+ );
12342
+ addFileToFilesToBeRenamed(
12343
+ `${rootDir}/assets`,
12344
+ originalUrlValue,
12345
+ hashedUrlValue,
12346
+ fileRenames
12347
+ );
12348
+ } catch {
12349
+ console.log(
12350
+ `warning: could not hash canvaskit.wasm resource because it was not found at ${originalUrlValue}`
12351
+ );
12352
+ }
12353
+ }
12354
+ }
12355
+ }
12356
+ if (ancestors.length >= 5) {
12357
+ const maybeProperty = ancestors.slice(-2)[0];
12358
+ if (maybeProperty.type === "Property") {
12359
+ const property = maybeProperty;
12360
+ if (property.key.type === "Identifier" && property.key.name == "url") {
12361
+ const maybeObjExpression = ancestors.slice(-3)[0];
12362
+ if (maybeObjExpression.type === "ObjectExpression") {
12363
+ const objExpression = maybeObjExpression;
12364
+ const properties = objExpression.properties.filter((p) => p.type === "Property").map((p) => p);
12365
+ const identifiers = properties.filter((p) => p.key.type === "Identifier").map((p) => p.key.name);
12366
+ const urlFontAssetProperties = ["fontName", "url"];
12367
+ const propCount = identifiers.filter(
12368
+ (i) => urlFontAssetProperties.indexOf(i) !== -1
12369
+ ).length;
12370
+ if (propCount === 2) {
12371
+ const maybeArrayExpression = ancestors.slice(-4)[0];
12372
+ if (maybeArrayExpression.type === "ArrayExpression") {
12373
+ const maybeProperty2 = ancestors.slice(-5)[0];
12374
+ if (maybeProperty2.type === "Property") {
12375
+ const property2 = maybeProperty2;
12376
+ if (property2.key.type === "Identifier" && property2.key.name == "fonts") {
12377
+ const literal = node;
12378
+ const originalUrlValue = literal.value;
12379
+ const optionsDeclarator = ancestors.slice(
12380
+ -7
12381
+ )[0];
12382
+ const objExpression2 = optionsDeclarator.init;
12383
+ const properties2 = objExpression2.properties;
12384
+ const gameIdProperty = properties2.filter(
12385
+ (p) => p.key.type === "Identifier" && p.key.name == "id"
12386
+ )[0];
12387
+ const gameId = gameIdProperty.value.value;
12514
12388
  try {
12515
- link.attribs["href"] = addHashToUrl(link.attribs["href"], rootDir);
12516
- addFileToFilesToBeRenamed(rootDir, originalUrl, link.attribs["href"], fileRenames);
12517
- }
12518
- catch (_b) {
12519
- console.log(`warning: could not hash css resource because it was not found at ${link.attribs["href"]}`);
12389
+ const hashedUrlValue = addHashToUrl(
12390
+ originalUrlValue,
12391
+ path.join(rootDir, "assets", gameId),
12392
+ cwd
12393
+ );
12394
+ literal.value = literal.value.replace(
12395
+ originalUrlValue,
12396
+ hashedUrlValue
12397
+ );
12398
+ literal.raw = literal.raw.replace(
12399
+ originalUrlValue,
12400
+ hashedUrlValue
12401
+ );
12402
+ addFileToFilesToBeRenamed(
12403
+ path.join(rootDir, "assets", gameId),
12404
+ originalUrlValue,
12405
+ hashedUrlValue,
12406
+ fileRenames
12407
+ );
12408
+ } catch {
12520
12409
  }
12410
+ }
12521
12411
  }
12522
- });
12523
- // handle the script link to index.js
12524
- const scripts = selectAll("script", dom);
12525
- scripts
12526
- .map((script) => script)
12527
- .forEach((script) => {
12528
- var _a;
12529
- if (script.attribs["src"] === "./index.js" &&
12530
- !(((_a = script.attribs["hash"]) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === "false")) {
12412
+ }
12413
+ }
12414
+ }
12415
+ }
12416
+ }
12417
+ }
12418
+ if (ancestors.length >= 5) {
12419
+ const maybeProperty = ancestors.slice(-2)[0];
12420
+ if (maybeProperty.type === "Property") {
12421
+ const property = maybeProperty;
12422
+ if (property.key.type === "Identifier" && property.key.name == "url") {
12423
+ const maybeObjExpression = ancestors.slice(-3)[0];
12424
+ if (maybeObjExpression.type === "ObjectExpression") {
12425
+ const objExpression = maybeObjExpression;
12426
+ const properties = objExpression.properties.filter((p) => p.type === "Property").map((p) => p);
12427
+ const identifiers = properties.filter((p) => p.key.type === "Identifier").map((p) => p.key.name);
12428
+ const urlBrowserImageProperties = [
12429
+ "imageName",
12430
+ "height",
12431
+ "width",
12432
+ "url"
12433
+ ];
12434
+ const propCount = identifiers.filter(
12435
+ (i) => urlBrowserImageProperties.indexOf(i) !== -1
12436
+ ).length;
12437
+ if (propCount === 4) {
12438
+ const maybeArrayExpression = ancestors.slice(-4)[0];
12439
+ if (maybeArrayExpression.type === "ArrayExpression") {
12440
+ const maybeProperty2 = ancestors.slice(-5)[0];
12441
+ if (maybeProperty2.type === "Property") {
12442
+ const property2 = maybeProperty2;
12443
+ if (property2.key.type === "Identifier" && property2.key.name == "images") {
12444
+ const literal = node;
12445
+ const originalUrlValue = literal.value;
12446
+ const optionsDeclarator = ancestors.slice(
12447
+ -7
12448
+ )[0];
12449
+ const objExpression2 = optionsDeclarator.init;
12450
+ const properties2 = objExpression2.properties;
12451
+ const gameIdProperty = properties2.filter(
12452
+ (p) => p.key.type === "Identifier" && p.key.name == "id"
12453
+ )[0];
12454
+ const gameId = gameIdProperty.value.value;
12531
12455
  try {
12532
- const hashedFilename = addHashToUrl("index.js", rootDir);
12533
- script.attribs["src"] = "./" + path.basename(hashedFilename);
12534
- addFileToFilesToBeRenamed(rootDir, "index.js", hashedFilename, fileRenames);
12535
- }
12536
- catch (_b) {
12537
- console.log(`warning: could not hash index.js because it was not found at ${indexJsFile}`);
12456
+ const hashedUrlValue = addHashToUrl(
12457
+ originalUrlValue,
12458
+ path.join(rootDir, "assets", gameId),
12459
+ cwd
12460
+ );
12461
+ literal.value = literal.value.replace(
12462
+ originalUrlValue,
12463
+ hashedUrlValue
12464
+ );
12465
+ literal.raw = literal.raw.replace(
12466
+ originalUrlValue,
12467
+ hashedUrlValue
12468
+ );
12469
+ addFileToFilesToBeRenamed(
12470
+ path.join(rootDir, "assets", gameId),
12471
+ originalUrlValue,
12472
+ hashedUrlValue,
12473
+ fileRenames
12474
+ );
12475
+ } catch {
12538
12476
  }
12477
+ }
12539
12478
  }
12540
- });
12541
- yield writeFile(indexHtmlFile, render(dom));
12542
- fileRenames.forEach((fr) => {
12543
- // we must check existsSync because a file may be included twice
12544
- if (existsSync(fr.original)) {
12545
- renameSync(fr.original, fr.new);
12546
- }
12547
- });
12548
- yield writeHashManifest(rootDir, fileRenames);
12549
- });
12550
- },
12551
- },
12552
- };
12479
+ }
12480
+ }
12481
+ }
12482
+ }
12483
+ }
12484
+ }
12485
+ }
12486
+ });
12487
+ const indexJsCode = generate(ast);
12488
+ await writeFile(indexJsFile, indexJsCode);
12489
+ const rawHtml = await readFile(indexHtmlFile, "utf-8");
12490
+ const dom = parseDocument(rawHtml);
12491
+ const links = selectAll("link", dom);
12492
+ links.map((link) => link).forEach((link) => {
12493
+ if (link.attribs["href"].endsWith(".css") && !(link.attribs["hash"]?.toLowerCase() === "false")) {
12494
+ const originalUrl = link.attribs["href"];
12495
+ try {
12496
+ link.attribs["href"] = addHashToUrl(
12497
+ link.attribs["href"],
12498
+ rootDir,
12499
+ cwd
12500
+ );
12501
+ addFileToFilesToBeRenamed(
12502
+ rootDir,
12503
+ originalUrl,
12504
+ link.attribs["href"],
12505
+ fileRenames
12506
+ );
12507
+ } catch {
12508
+ console.log(
12509
+ `warning: could not hash css resource because it was not found at ${link.attribs["href"]}`
12510
+ );
12511
+ }
12512
+ }
12513
+ });
12514
+ const scripts = selectAll("script", dom);
12515
+ scripts.map((script) => script).forEach((script) => {
12516
+ if (script.attribs["src"] === "./index.js" && !(script.attribs["hash"]?.toLowerCase() === "false")) {
12517
+ try {
12518
+ const hashedFilename = addHashToUrl("index.js", rootDir, cwd);
12519
+ script.attribs["src"] = "./" + path.basename(hashedFilename);
12520
+ addFileToFilesToBeRenamed(
12521
+ rootDir,
12522
+ "index.js",
12523
+ hashedFilename,
12524
+ fileRenames
12525
+ );
12526
+ } catch {
12527
+ console.log(
12528
+ `warning: could not hash index.js because it was not found at ${indexJsFile}`
12529
+ );
12530
+ }
12531
+ }
12532
+ });
12533
+ await writeFile(indexHtmlFile, render(dom));
12534
+ fileRenames.forEach((fr) => {
12535
+ if (existsSync(path.join(cwd, fr.original))) {
12536
+ renameSync(path.join(cwd, fr.original), path.join(cwd, fr.new));
12537
+ }
12538
+ });
12539
+ await writeHashManifest(rootDir, fileRenames, cwd);
12540
+ }
12541
+ }
12542
+ };
12553
12543
  }
12554
12544
  const getFileHash = (filePath) => {
12555
- const buffer = readFileSync(filePath);
12556
- const hash = createHash("md5").update(buffer).digest("hex");
12557
- return hash.slice(0, HASH_CHARACTER_LENGTH);
12545
+ const buffer = readFileSync(filePath);
12546
+ const hash = createHash("md5").update(buffer).digest("hex");
12547
+ return hash.slice(0, HASH_CHARACTER_LENGTH);
12548
+ };
12549
+ const addHashToUrl = (url, rootDir, cwd = "") => {
12550
+ const ext = path.extname(url);
12551
+ const hash = getFileHash(path.join(cwd, rootDir, url));
12552
+ if (ext) {
12553
+ url = url.replace(ext, `.${hash}${ext}`);
12554
+ } else {
12555
+ url = url + `.${hash}`;
12556
+ }
12557
+ return url;
12558
12558
  };
12559
- const addHashToUrl = (url, rootDir) => {
12560
- const ext = path.extname(url);
12561
- const hash = getFileHash(path.join(rootDir, url));
12562
- if (ext) {
12563
- url = url.replace(ext, `.${hash}${ext}`);
12559
+ async function writeHashManifest(rootDir, fileRenames, cwd = "") {
12560
+ const manifestFilename = path.join(cwd, rootDir, "hash-manifest.json");
12561
+ const manifest = {};
12562
+ fileRenames.forEach((fr) => {
12563
+ let originalName = fr.original.replace(rootDir + path.sep, "");
12564
+ let hashedName = fr.new.replace(rootDir + path.sep, "");
12565
+ if (process.platform === "win32") {
12566
+ originalName = originalName.replace(/\\/g, "/");
12567
+ hashedName = hashedName.replace(/\\/g, "/");
12564
12568
  }
12565
- else {
12566
- url = url + `.${hash}`;
12567
- }
12568
- return url;
12569
- };
12570
- function writeHashManifest(rootDir, fileRenames) {
12571
- return __awaiter(this, void 0, void 0, function* () {
12572
- const manifestFilename = path.join(rootDir, "hash-manifest.json");
12573
- const manifest = {};
12574
- fileRenames.forEach((fr) => {
12575
- let originalName = fr.original.replace(rootDir + path.sep, "");
12576
- let hashedName = fr.new.replace(rootDir + path.sep, "");
12577
- if (process.platform === "win32") {
12578
- originalName = originalName.replace(/\\/g, "/");
12579
- hashedName = hashedName.replace(/\\/g, "/");
12580
- }
12581
- manifest[originalName] = hashedName;
12582
- });
12583
- const manifestString = JSON.stringify(manifest, null, 2);
12584
- yield writeFile(manifestFilename, manifestString);
12585
- });
12569
+ manifest[originalName] = hashedName;
12570
+ });
12571
+ const manifestString = JSON.stringify(manifest, null, 2);
12572
+ await writeFile(manifestFilename, manifestString);
12586
12573
  }
12587
12574
  function addFileToFilesToBeRenamed(rootDir, originalUrlValue, hashedUrlValue, fileRenames) {
12588
- const filename = path.join(rootDir, originalUrlValue);
12589
- const hashedFilename = path.join(rootDir, hashedUrlValue);
12590
- fileRenames.push({
12591
- original: filename,
12592
- new: hashedFilename,
12593
- });
12575
+ const filename = path.join(rootDir, originalUrlValue);
12576
+ const hashedFilename = path.join(rootDir, hashedUrlValue);
12577
+ fileRenames.push({
12578
+ original: filename,
12579
+ new: hashedFilename
12580
+ });
12594
12581
  }
12595
12582
 
12596
12583
  function makeM2c2kitServiceWorker(rootDir, additionalFiles) {
12597
- let inputFile = "";
12598
- return {
12599
- name: "make-m2c2kit-serviceworker",
12600
- buildStart: {
12601
- handler(options) {
12602
- if (options.input) {
12603
- if (typeof options.input === "string") {
12604
- inputFile = path.resolve(options.input);
12605
- }
12606
- else if (Array.isArray(options.input)) {
12607
- inputFile = path.resolve(options.input[0]);
12608
- }
12609
- else {
12610
- throw new Error("Could not determine input file when trying to add service worker code.");
12611
- }
12612
- }
12613
- },
12614
- },
12615
- transform: {
12616
- handler(code, id) {
12617
- if (id === inputFile) {
12618
- if (code.includes("//# sourceMappingURL")) {
12619
- code = code.replace("//# sourceMappingURL", serviceWorkerRegistrationCode + "\n//# sourceMappingURL");
12620
- }
12621
- }
12622
- return code;
12623
- },
12624
- },
12625
- closeBundle: {
12626
- sequential: true,
12627
- handler() {
12628
- return __awaiter(this, void 0, void 0, function* () {
12629
- // this is the path of our @m2c2kit/build-helpers package
12630
- // our sw.js template is stored under the "assets" folder here
12631
- const packageHomeFolderPath = path.dirname(fileURLToPath(import.meta.url));
12632
- let swContents = (yield readFile(path.join(packageHomeFolderPath, "assets", "sw.js"))).toString();
12633
- const manifestFilename = path.join(rootDir, "hash-manifest.json");
12634
- const manifestContents = (yield readFile(manifestFilename)).toString();
12635
- const manifest = JSON.parse(manifestContents);
12636
- const hashedFilenames = Object.values(manifest);
12637
- let replacementString = hashedFilenames.map((f) => `"${f}"`).join(",");
12638
- if (additionalFiles) {
12639
- replacementString =
12640
- replacementString +
12641
- "," +
12642
- additionalFiles.map((f) => `"${f}"`).join(",");
12643
- }
12644
- swContents = swContents.replace('"_-_ADDITIONAL_RESOURCES_TO_CACHE_-_"', replacementString);
12645
- const swDestinationFilename = path.join(rootDir, "sw.js");
12646
- yield writeFile(swDestinationFilename, swContents);
12647
- });
12648
- },
12649
- },
12650
- };
12584
+ let inputFile = "";
12585
+ return {
12586
+ name: "make-m2c2kit-serviceworker",
12587
+ buildStart: {
12588
+ handler(options) {
12589
+ if (options.input) {
12590
+ if (typeof options.input === "string") {
12591
+ inputFile = path.resolve(options.input);
12592
+ } else if (Array.isArray(options.input)) {
12593
+ inputFile = path.resolve(options.input[0]);
12594
+ } else {
12595
+ throw new Error(
12596
+ "Could not determine input file when trying to add service worker code."
12597
+ );
12598
+ }
12599
+ }
12600
+ }
12601
+ },
12602
+ transform: {
12603
+ handler(code, id) {
12604
+ if (id === inputFile) {
12605
+ if (code.includes("//# sourceMappingURL")) {
12606
+ code = code.replace(
12607
+ "//# sourceMappingURL",
12608
+ serviceWorkerRegistrationCode + "\n//# sourceMappingURL"
12609
+ );
12610
+ }
12611
+ }
12612
+ return code;
12613
+ }
12614
+ },
12615
+ closeBundle: {
12616
+ sequential: true,
12617
+ async handler() {
12618
+ const packageHomeFolderPath = path.dirname(
12619
+ fileURLToPath(import.meta.url)
12620
+ );
12621
+ let swContents = (await readFile(path.join(packageHomeFolderPath, "assets", "sw.js"))).toString();
12622
+ const manifestFilename = path.join(rootDir, "hash-manifest.json");
12623
+ const manifestContents = (await readFile(manifestFilename)).toString();
12624
+ const manifest = JSON.parse(manifestContents);
12625
+ const hashedFilenames = Object.values(manifest);
12626
+ let replacementString = hashedFilenames.map((f) => `"${f}"`).join(",");
12627
+ if (additionalFiles) {
12628
+ replacementString = replacementString + "," + additionalFiles.map((f) => `"${f}"`).join(",");
12629
+ }
12630
+ swContents = swContents.replace(
12631
+ '"_-_ADDITIONAL_RESOURCES_TO_CACHE_-_"',
12632
+ replacementString
12633
+ );
12634
+ const swDestinationFilename = path.join(rootDir, "sw.js");
12635
+ await writeFile(swDestinationFilename, swContents);
12636
+ }
12637
+ }
12638
+ };
12651
12639
  }
12652
12640
  const serviceWorkerRegistrationCode = `const registerServiceWorker = async () => {
12653
12641
  if ("serviceWorker" in navigator) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@m2c2kit/build-helpers",
3
- "version": "0.3.5",
3
+ "version": "0.3.7",
4
4
  "description": "",
5
5
  "module": "dist/index.js",
6
6
  "type": "module",
@@ -11,28 +11,29 @@
11
11
  ],
12
12
  "devDependencies": {
13
13
  "@rollup/plugin-commonjs": "25.0.0",
14
- "@rollup/plugin-node-resolve": "15.0.2",
15
- "@rollup/plugin-typescript": "11.1.1",
14
+ "@rollup/plugin-node-resolve": "15.1.0",
16
15
  "@types/estree": "1.0.1",
17
16
  "@types/findup-sync": "4.0.2",
17
+ "cpy": "10.1.0",
18
18
  "cpy-cli": "4.2.0",
19
19
  "findup-sync": "5.0.0",
20
20
  "rimraf": "5.0.1",
21
21
  "rollup": "3.21.0",
22
22
  "rollup-plugin-copy": "^3.4.0",
23
23
  "rollup-plugin-dts": "5.3.0",
24
- "typescript": "5.0.4"
24
+ "rollup-plugin-esbuild": "5.0.0",
25
+ "typescript": "5.1.3"
25
26
  },
26
27
  "scripts": {
27
- "build": "npm run clean && rollup -c && cpy assets dist",
28
+ "build": "npm run clean && tsc && rollup -c",
28
29
  "clean": "rimraf build dist .rollup.cache tsconfig.tsbuildinfo",
29
- "test": "echo \"Error: no test specified\" && exit 1"
30
+ "test": "cd ../.. && npx env-cmd -f .env.jest jest --selectProjects @m2c2kit/build-helpers"
30
31
  },
31
32
  "license": "MIT",
32
33
  "dependencies": {
33
34
  "acorn": "8.8.2",
34
35
  "acorn-walk": "8.2.0",
35
- "astring": "1.8.4",
36
+ "astring": "1.8.6",
36
37
  "css-select": "5.1.0",
37
38
  "dom-serializer": "2.0.0",
38
39
  "domhandler": "5.0.3",