@pi-r/chrome 0.12.4 → 0.13.0

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.
@@ -7,7 +7,7 @@ const { escapePattern, manageGlobalObject } = require('@e-mc/types');
7
7
  const { PATTERN_PARENTHESIS, findClosingIndex, getNewlineString, replaceMatch, spliceSource, splitEnclosing } = require('@pi-r/chrome/util');
8
8
  const PATTERN_STRING_SOME = '(?:\\s|' + DomWriter.PATTERN_COMMENT + '|`[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}`)*';
9
9
  const PATTERN_STRING_MANY = PATTERN_STRING_SOME.slice(0, -1) + '+';
10
- const PATTERN_VARIABLE = '--(?:[-\\w]+|\\\\\\S|[^\\u0000-\\u007F]+)+';
10
+ const PATTERN_VARIABLE = '--(?:[\\w-]+|\\\\\\S|[^\\u0000-\\u007F])+';
11
11
  const CACHE_ATRULES = Object.create(null);
12
12
  const CACHE_QUERY = new Map();
13
13
  const CACHE_SELECTOR = new Map();
package/index.js CHANGED
@@ -4,14 +4,15 @@ const path = require('node:path');
4
4
  const fs = require('node:fs');
5
5
  const { randomUUID } = require('node:crypto');
6
6
  const { imageSize } = require('image-size');
7
+ const { JSONPath } = require('jsonpath-plus');
7
8
  const Cloud = require('@e-mc/cloud');
8
9
  const { ClientDb } = require('@e-mc/core');
9
10
  const { DomWriter, HtmlElement, IGNORE_FLAG } = require('@e-mc/document/parse/dom');
10
11
  const { ERR_CODE, FILE_TYPE, WATCH_EVENT, asExt, asFunction, cascadeObject, cloneFlag, cloneObject, coerceObject, createAbortError, errorMessage, errorValue, escapePattern, existsFlag, getEncoding, ignoreFlag, isArray, isEmpty, isError, isErrorCode, isFunction, isObject, isPlainObject, isString, modifiedFlag, processFlag, randomString, renameExt, requireESM, watchFlag } = require('@e-mc/types');
11
12
  const { isEqual, setInitialValue } = require('@e-mc/document/asset');
12
- const { appendSuffix, getHashData, getModuleName, getNewline, hasValue, spliceString, trimQuote } = require('@e-mc/document/util');
13
+ const { appendSuffix, getHashData, getModuleName, getNewline, hasValue, spliceMatch, spliceString, splitEnclosing, trimQuote } = require('@e-mc/document/util');
13
14
  const { SourceMap } = require('@e-mc/document/transform');
14
- const { PATTERN_PARENTHESIS, findClosingIndex, getNewlineString, isSpace, replaceMatch } = require('@pi-r/chrome/util');
15
+ const { findClosingIndex, getNewlineString, isSpace, replaceMatch } = require('@pi-r/chrome/util');
15
16
  const Document = require('@e-mc/document');
16
17
  const kChrome = Symbol.for('chrome:constructor');
17
18
  const CACHE_DBRESULT = new Map();
@@ -32,7 +33,7 @@ const REGEXP_BLOCKEXCLUDE = new RegExp(`(\\s*)<!--\\s*exclude\\s*:\\s*(?:start|e
32
33
  const REGEXP_STYLE = new RegExp(`(<style${DomWriter.PATTERN_TAGOPEN}*>)(.*?)(<\\/style\\s*>)`, 'gis');
33
34
  const REGEXP_SANITIZEALL = new RegExp(`(?:\\s+|\\b)data-(?:pathname|filename|intl-locales|(?:use|exclude)-[^=\\s]+)\\s*${DomWriter.PATTERN_ATTRVALUE}(?=(?:[^"<]+"[^"]*"|[^'<]+'[^']*')*[^<>]*>)`, 'g');
34
35
  const REGEXP_IMAGESET_URL = /(?:^|[^(])\s*("(\s*[^)][^"]*)"|'(\s*[^)][^"]*)')\s*(?:,|$|[^)][^,]*,?)/gd;
35
- const REGEXP_REPLACECSS = new RegExp(`^@import\\s+(?:url\\((?:[^)]|(?<=\\\\)\\))+\\)|"(?:[^"]|(?<=\\\\)")+"|'(?:[^']|(?<=\\\\)')+')((?:(?:\\s+|(?<=["'\\\\]))(?:(?:layer|supports)\\(${PATTERN_PARENTHESIS}\\)|layer\\b)){1,2})?(.*)$`, 'is');
36
+ const REGEXP_URLMODIFIER = /\b(?<!-)url\(\s*(["'])(.+)\1\s*((?:cross-origin|integrity|referrer-policy)\([^)]+\))\s*\)/gi;
36
37
  function removeNamespace(name, source, newline, comments) {
37
38
  if (source.includes('-' + name)) {
38
39
  const [a, b, c, d, e] = CACHE_DATASET[name] ||= [
@@ -324,8 +325,8 @@ const isHtmlOnly = (assets, items, htmlFile) => !items.find(item => item !== htm
324
325
  class SessionOutput {
325
326
  host;
326
327
  startTime;
327
- contentMap = {};
328
- bufferMap = {};
328
+ contentMap = Object.create(null);
329
+ bufferMap = Object.create(null);
329
330
  cacheMiss = [];
330
331
  hashed = new Set();
331
332
  productionRelease;
@@ -544,7 +545,7 @@ class ChromeDocument extends Document {
544
545
  const mapped = [];
545
546
  for (const localDir in serverRootMapping) {
546
547
  const subDir = path.normalize(serverRootMapping[localDir]);
547
- const destDir = validateDir(Document.normalizePath(subDir.endsWith(path.sep) ? path.join(outputDir, subDir, localDir) : path.join(outputDir, subDir), true));
548
+ const destDir = validateDir(Document.normalizePath(subDir.at(-1) === path.sep ? path.join(outputDir, subDir, localDir) : path.join(outputDir, subDir), true));
548
549
  if (destDir.startsWith(outputDir)) {
549
550
  if (this.canWrite(destDir)) {
550
551
  mapped.push([Document.normalizePath(localDir, true), destDir, 0]);
@@ -1116,23 +1117,24 @@ class ChromeDocument extends Document {
1116
1117
  }
1117
1118
  catch {
1118
1119
  }
1119
- continue;
1120
1120
  }
1121
- if (relative) {
1122
- if (ignorePath) {
1123
- continue;
1121
+ else {
1122
+ if (relative) {
1123
+ if (ignorePath) {
1124
+ continue;
1125
+ }
1126
+ pathname = path.join(localFile, from);
1124
1127
  }
1125
- pathname = path.join(localFile, from);
1126
- }
1127
- else if (!(pathname = this.findSourceRoot(from)) || !Document.isPath(pathname)) {
1128
- const asset = host.findAsset(from);
1129
- if (!asset || asset.invalid) {
1130
- continue;
1128
+ else if (!(pathname = this.findSourceRoot(from)) || !Document.isPath(pathname)) {
1129
+ const asset = host.findAsset(from);
1130
+ if (!asset || asset.invalid) {
1131
+ continue;
1132
+ }
1133
+ pathname = asset.localUri;
1134
+ }
1135
+ if (pathname && Document.isPath(pathname)) {
1136
+ output = spliceString(output, a, b, quote + pathname + quote, REGEXP_IMPORTMODULE);
1131
1137
  }
1132
- pathname = asset.localUri;
1133
- }
1134
- if (pathname && Document.isPath(pathname)) {
1135
- output = spliceString(output, a, b, quote + pathname + quote, REGEXP_IMPORTMODULE);
1136
1138
  }
1137
1139
  }
1138
1140
  REGEXP_IMPORTMODULE.lastIndex = 0;
@@ -1234,21 +1236,35 @@ class ChromeDocument extends Document {
1234
1236
  replaceContent(source, statement, mimeType) {
1235
1237
  switch (mimeType) {
1236
1238
  case "text/css": {
1237
- const match = REGEXP_REPLACECSS.exec((typeof statement === 'string' ? statement : statement[0]).trim());
1238
- if (match) {
1239
- const trailing = match[1];
1239
+ let content = (typeof statement === 'string' ? statement : statement[0]).trim();
1240
+ const url = /^@import\s+(?:(url\()|"(?:[^"]|(?<=\\)")+"|'(?:[^']|(?<=\\)')+')(?!$)/i.exec(content);
1241
+ if (url) {
1242
+ if (!url[1]) {
1243
+ content = content.slice(url[0].length);
1244
+ }
1245
+ else if (!(content = splitEnclosing(content, 1)[2])) {
1246
+ return;
1247
+ }
1248
+ const items = splitEnclosing(content, /(?<=[\s)"'])(?:layer|supports)/gi);
1240
1249
  let layer, supports;
1241
- if (trailing) {
1242
- const name = /\blayer(?:\(([^)]+)\)|\s|$)/i.exec(trailing);
1243
- if (name) {
1244
- layer = name[1]?.trim() || '';
1245
- supports = spliceString(trailing, name.index, name.index + name[0].length, '').trim();
1250
+ for (let i = 0; i < items.length; ++i) {
1251
+ const item = items[i];
1252
+ let match = /^(layer|supports)\((.+)\)$/i.exec(item);
1253
+ if (match) {
1254
+ if (match[1].toLowerCase() === 'layer') {
1255
+ layer = match[2].trim();
1256
+ }
1257
+ else {
1258
+ supports = match[2].trim();
1259
+ }
1260
+ items[i] = '';
1246
1261
  }
1247
- else {
1248
- supports = trailing.trimStart();
1262
+ else if (match = /^\s*layer(?:\s+|$)/i.exec(item)) {
1263
+ layer ??= '';
1264
+ items[i] = item.slice(match[0].length);
1249
1265
  }
1250
1266
  }
1251
- const query = match[2].trim().replace(/\s*;+$/, '');
1267
+ const query = items.join('').trim().replace(/\s*;+$/, '');
1252
1268
  if (typeof layer === 'string' || supports || query) {
1253
1269
  const newline = getNewline(source);
1254
1270
  let result = supports ? `@supports (${normalizeSpace(supports)}) {` + newline : '';
@@ -1999,29 +2015,22 @@ class ChromeDocument extends Document {
1999
2015
  else if (item.fallback !== undefined) {
2000
2016
  data = item.fallback;
2001
2017
  }
2002
- else if (!item.query) {
2003
- return null;
2004
- }
2005
2018
  }
2006
2019
  if (data) {
2007
2020
  if (isString(item.query)) {
2008
- const pkg = item.query.startsWith('$') ? Document.checkSemVer('jsonpath-plus', 5) ? null : 'jsonpath' : 'jmespath';
2021
+ const jp = item.query.at(0) === '$';
2009
2022
  try {
2010
- if (pkg === 'jmespath') {
2011
- data = require(pkg).search(data, item.query);
2012
- }
2013
- else if (pkg === 'jsonpath') {
2014
- data = require(pkg).query(data, item.query);
2023
+ if (jp) {
2024
+ data = JSONPath({ path: item.query, json: data });
2015
2025
  }
2016
2026
  else {
2017
- const { JSONPath } = require('jsonpath-plus');
2018
- data = JSONPath({ path: item.query, json: data });
2027
+ data = require('jmespath').search(data, item.query);
2019
2028
  }
2020
2029
  }
2021
2030
  catch (err) {
2022
2031
  this.abort(item.source);
2023
- if (pkg) {
2024
- this.checkPackage(err, pkg, ["Unable to filter results", item.source]);
2032
+ if (!jp) {
2033
+ this.checkPackage(err, 'jmespath', ["Unable to filter results", item.source]);
2025
2034
  }
2026
2035
  }
2027
2036
  }
@@ -2029,12 +2038,18 @@ class ChromeDocument extends Document {
2029
2038
  coerceObject(data);
2030
2039
  }
2031
2040
  }
2032
- return Array.isArray(data) ? data : isObject(data) ? [data] : null;
2041
+ if (Array.isArray(data)) {
2042
+ return data;
2043
+ }
2044
+ if (typeof data === 'object') {
2045
+ return [data];
2046
+ }
2047
+ throw errorMessage(item.source, "Invalid value", Cloud.asString(data) || "Unknown");
2033
2048
  };
2034
2049
  const setElements = async (sourceSet) => {
2035
2050
  const db = this.Db;
2036
2051
  const cloud = host.Cloud;
2037
- const batchMap = Object.create(null);
2052
+ const batchMap = {};
2038
2053
  const batchGroup = [];
2039
2054
  for (const item of sourceSet) {
2040
2055
  if (!item.element) {
@@ -2362,12 +2377,6 @@ class ChromeDocument extends Document {
2362
2377
  }
2363
2378
  switch (type) {
2364
2379
  case 'mongodb':
2365
- for (let j = 0; j < length; ++j) {
2366
- const cmd = batch[j];
2367
- if (cmd.cascade) {
2368
- cmd.cacheObjectKey ||= cmd.cascade;
2369
- }
2370
- }
2371
2380
  break;
2372
2381
  case 'redis':
2373
2382
  for (let j = 0; j < length; ++j) {
@@ -2399,12 +2408,6 @@ class ChromeDocument extends Document {
2399
2408
  search.schema = undefined;
2400
2409
  }
2401
2410
  }
2402
- if (cmd.key) {
2403
- const { cascade, query } = cmd;
2404
- if (cascade || query) {
2405
- cmd.cacheObjectKey ||= (cascade ? cascade : '') + '_' + (query ? Document.asString(query, true) : '');
2406
- }
2407
- }
2408
2411
  }
2409
2412
  break;
2410
2413
  default:
@@ -3037,10 +3040,15 @@ class ChromeDocument extends Document {
3037
3040
  format && (hasValue(format, 'base64') || hasValue(format, 'woff') || hasValue(format, 'woff2')) ? true : false);
3038
3041
  }
3039
3042
  async processData(item, result, method, validate) {
3040
- let callback;
3043
+ let callback = null;
3041
3044
  if (typeof method === 'string') {
3042
3045
  const template = this.findDataValue(method) || method;
3043
- callback = this.hasEval('userconfig') ? Document.parseFunction(template, { absolute: this.hasEval('absolute'), default: true }) : asFunction(template);
3046
+ if (this.hasEval('userconfig')) {
3047
+ callback = Document.parseFunction(template, { absolute: this.hasEval('absolute'), default: true });
3048
+ }
3049
+ else if (this.hasEval('function')) {
3050
+ callback = asFunction(template);
3051
+ }
3044
3052
  }
3045
3053
  else {
3046
3054
  callback = method;
@@ -3139,7 +3147,15 @@ class ChromeDocument extends Document {
3139
3147
  const isJs = type === 'js';
3140
3148
  const imageSet = /(?:\b(?<!-)|(?<!-)-webkit-)image-set\(/gi;
3141
3149
  const pattern = isJs ? /\b(?<!-)import\(/g : !isHtml ? /(?:\b(?<!-)url\(|@import\s+(["']))/gi : /(?:\b(?<!-)url\(|@import\s+(["'])|\b(?<!-)(href)\s*=)/gi;
3142
- let length = source.length, modified = false, match;
3150
+ let length = source.length, modified = false, replaced, match;
3151
+ if (!isJs) {
3152
+ while (match = REGEXP_URLMODIFIER.exec(source)) {
3153
+ const placeholder = randomUUID();
3154
+ (replaced ||= {})[placeholder] = match[3];
3155
+ source = spliceMatch(source, match, `url(${match[1] + match[2] + match[1]} ${placeholder})`, REGEXP_URLMODIFIER);
3156
+ }
3157
+ REGEXP_URLMODIFIER.lastIndex = 0;
3158
+ }
3143
3159
  while (match = imageSet.exec(source)) {
3144
3160
  const startIndex = match.index + match[0].length;
3145
3161
  const endIndex = findClosingIndex(source, startIndex, '(');
@@ -3183,39 +3199,40 @@ class ChromeDocument extends Document {
3183
3199
  }
3184
3200
  }
3185
3201
  j = i + 1;
3186
- continue;
3187
3202
  }
3188
- if (!quote && (ch === '"' || ch === "'" || ch === '`' && isJs) && (isHref || !content.trim())) {
3189
- quote = ch;
3190
- }
3191
- else if (isHref && (ch === '#' || !quote && isSpace(ch)) && content.trim()) {
3192
- break pass;
3193
- }
3194
- else if (ch === ')') {
3195
- if (isUrl) {
3196
- if (quote && closed !== -1 || !quote && (isJs || source[i - 1] !== '\\')) {
3197
- break;
3203
+ else {
3204
+ if (!quote && (ch === '"' || ch === "'" || ch === '`' && isJs) && (isHref || !content.trim())) {
3205
+ quote = ch;
3206
+ }
3207
+ else if (isHref && (ch === '#' || !quote && isSpace(ch)) && content.trim()) {
3208
+ break pass;
3209
+ }
3210
+ else if (ch === ')') {
3211
+ if (isUrl) {
3212
+ if (quote && closed !== -1 || !quote && (isJs || source[i - 1] !== '\\')) {
3213
+ break;
3214
+ }
3215
+ j = i;
3198
3216
  }
3199
- j = i;
3200
3217
  }
3201
- }
3202
- else if (j !== -1 && (ch === '\n' ||
3203
- isText && (ch === ';' || ch === '{') ||
3204
- isHref && (ch === '=' || ch === '>') ||
3205
- isUrl && (ch === ';' ||
3206
- !isJs && (!isHtml && (ch === ':' && !isJs || ch === '}') ||
3207
- isHtml && (ch === '"' || ch === "'" || ch === ':' || ch === '>'))))) {
3208
- if (closed !== -1) {
3209
- i = closed;
3218
+ else if (j !== -1 && (ch === '\n' ||
3219
+ isText && (ch === ';' || ch === '{') ||
3220
+ isHref && (ch === '=' || ch === '>') ||
3221
+ isUrl && (ch === ';' ||
3222
+ !isJs && (!isHtml && (ch === ':' && !isJs || ch === '}') ||
3223
+ isHtml && (ch === '"' || ch === "'" || ch === ':' || ch === '>'))))) {
3224
+ if (closed !== -1) {
3225
+ i = closed;
3226
+ }
3227
+ else {
3228
+ i = j;
3229
+ content = content.slice(0, i);
3230
+ }
3231
+ break;
3210
3232
  }
3211
- else {
3212
- i = j;
3213
- content = content.slice(0, i);
3233
+ if (closed === -1) {
3234
+ content += ch;
3214
3235
  }
3215
- break;
3216
- }
3217
- if (closed === -1) {
3218
- content += ch;
3219
3236
  }
3220
3237
  }
3221
3238
  const setOutputURL = (value, asset) => {
@@ -3394,6 +3411,9 @@ class ChromeDocument extends Document {
3394
3411
  }
3395
3412
  }
3396
3413
  if (modified) {
3414
+ for (const placeholder in replaced) {
3415
+ source = source.replace(placeholder, replaced[placeholder]);
3416
+ }
3397
3417
  if (related.length > 0) {
3398
3418
  const previous = this.related.get(file);
3399
3419
  if (previous) {
@@ -3480,7 +3500,7 @@ class ChromeDocument extends Document {
3480
3500
  }
3481
3501
  }
3482
3502
  if ((!this.productionRelease || this.productionIncremental) && !modifiedFlag(file.flags)) {
3483
- options.cacheData = { uri: !file.exported ? uri : undefined, etag: !file.modified ? file.etag : undefined, encoding: file.encoding };
3503
+ options.cacheData = Object.freeze({ uri: !file.exported ? uri : undefined, etag: !file.modified ? file.etag : undefined, encoding: file.encoding });
3484
3504
  }
3485
3505
  return options;
3486
3506
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-r/chrome",
3
- "version": "0.12.4",
3
+ "version": "0.13.0",
4
4
  "description": "Chrome document constructor for E-mc.",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -17,10 +17,11 @@
17
17
  "license": "MIT",
18
18
  "homepage": "https://github.com/anpham6/pi-r#readme",
19
19
  "dependencies": {
20
- "@e-mc/cloud": "^0.14.5",
21
- "@e-mc/core": "^0.14.5",
22
- "@e-mc/document": "^0.14.5",
23
- "@e-mc/types": "^0.14.5",
24
- "image-size": "^2.0.2"
20
+ "@e-mc/cloud": "^0.14.6",
21
+ "@e-mc/core": "^0.14.6",
22
+ "@e-mc/document": "^0.14.6",
23
+ "@e-mc/types": "^0.14.6",
24
+ "image-size": "^2.0.2",
25
+ "jsonpath-plus": "^10.4.0"
25
26
  }
26
27
  }