projen-pipelines 0.3.22 → 0.3.23
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/.jsii +7 -4
- package/API.md +420 -0
- package/lib/assign-approver/base.js +3 -4
- package/lib/assign-approver/github.js +4 -4
- package/lib/awscdk/base.js +47 -5
- package/lib/awscdk/bash.js +2 -4
- package/lib/awscdk/github.js +13 -6
- package/lib/awscdk/gitlab.js +11 -6
- package/lib/drift/base.js +8 -4
- package/lib/drift/bash.js +3 -4
- package/lib/drift/detect-drift.js +3 -2
- package/lib/drift/github.js +5 -4
- package/lib/drift/gitlab.js +5 -4
- package/lib/drift/step.js +2 -4
- package/lib/release.js +5 -2
- package/lib/steps/amplify-deploy.step.js +5 -4
- package/lib/steps/artifact-steps.js +5 -6
- package/lib/steps/aws-assume-role.step.js +3 -4
- package/lib/steps/github-summary.step.js +3 -4
- package/lib/steps/package-manager-setup.step.js +4 -6
- package/lib/steps/registries.js +7 -8
- package/lib/steps/set-env.step.js +2 -1
- package/lib/steps/step.js +9 -10
- package/lib/versioning/computation.js +6 -8
- package/lib/versioning/config.js +3 -6
- package/lib/versioning/outputs.js +13 -16
- package/lib/versioning/setup.js +4 -4
- package/lib/versioning/strategy.js +4 -4
- package/lib/versioning/version-info.js +18 -9
- package/node_modules/@babel/code-frame/package.json +2 -2
- package/node_modules/@babel/helper-validator-identifier/package.json +1 -1
- package/node_modules/@nodable/entities/README.md +52 -1
- package/node_modules/@nodable/entities/package.json +4 -1
- package/node_modules/@nodable/entities/src/EntityDecoder.js +105 -4
- package/node_modules/@nodable/entities/src/EntityEncoder.js +79 -34
- package/node_modules/@nodable/entities/src/all-entities.js +53 -0
- package/node_modules/@nodable/entities/src/entities.js +0 -38
- package/node_modules/@nodable/entities/src/entityTries.js +48 -37
- package/node_modules/@nodable/entities/src/index.d.ts +93 -2
- package/node_modules/@nodable/entities/src/index.js +2 -2
- package/node_modules/anynum/LICENSE +21 -0
- package/node_modules/anynum/README.md +142 -0
- package/node_modules/anynum/anynum.js +135 -0
- package/node_modules/anynum/digitTable.js +116 -0
- package/node_modules/anynum/package.json +42 -0
- package/node_modules/dotgitignore/node_modules/brace-expansion/README.md +23 -1
- package/node_modules/dotgitignore/node_modules/brace-expansion/index.js +265 -86
- package/node_modules/dotgitignore/node_modules/brace-expansion/package.json +1 -1
- package/node_modules/fast-xml-builder/CHANGELOG.md +12 -0
- package/node_modules/fast-xml-builder/README.md +4 -1
- package/node_modules/fast-xml-builder/lib/fxb.cjs +1 -1
- package/node_modules/fast-xml-builder/lib/fxb.min.js +1 -1
- package/node_modules/fast-xml-builder/lib/fxb.min.js.map +1 -1
- package/node_modules/fast-xml-builder/package.json +6 -10
- package/node_modules/fast-xml-builder/src/fxb.js +34 -24
- package/node_modules/fast-xml-builder/src/orderedJs2Xml.js +18 -17
- package/node_modules/fast-xml-builder/src/util.js +10 -3
- package/node_modules/fast-xml-parser/CHANGELOG.md +34 -0
- package/node_modules/fast-xml-parser/README.md +10 -6
- package/node_modules/fast-xml-parser/lib/fxbuilder.min.js +1 -1
- package/node_modules/fast-xml-parser/lib/fxbuilder.min.js.map +1 -1
- package/node_modules/fast-xml-parser/lib/fxp.cjs +1 -1
- package/node_modules/fast-xml-parser/lib/fxp.d.cts +3 -1
- package/node_modules/fast-xml-parser/lib/fxp.min.js +1 -1
- package/node_modules/fast-xml-parser/lib/fxp.min.js.map +1 -1
- package/node_modules/fast-xml-parser/lib/fxparser.min.js +1 -1
- package/node_modules/fast-xml-parser/lib/fxparser.min.js.map +1 -1
- package/node_modules/fast-xml-parser/package.json +8 -10
- package/node_modules/fast-xml-parser/src/fxp.d.ts +5 -1
- package/node_modules/fast-xml-parser/src/xmlparser/DocTypeReader.js +16 -1
- package/node_modules/fast-xml-parser/src/xmlparser/OptionsBuilder.js +2 -1
- package/node_modules/fast-xml-parser/src/xmlparser/OrderedObjParser.js +36 -3
- package/node_modules/fast-xml-parser/src/xmlparser/xmlNode.js +14 -0
- package/node_modules/get-pkg-repo/node_modules/yargs/package.json +27 -26
- package/node_modules/get-pkg-repo/node_modules/yargs/yargs +5 -9
- package/node_modules/get-pkg-repo/node_modules/yargs/yargs.cjs +15 -0
- package/node_modules/hasown/CHANGELOG.md +7 -0
- package/node_modules/hasown/index.d.ts +0 -1
- package/node_modules/hasown/package.json +4 -5
- package/node_modules/is-unsafe/LICENSE +21 -0
- package/node_modules/is-unsafe/README.md +599 -0
- package/node_modules/is-unsafe/package.json +56 -0
- package/node_modules/is-unsafe/src/contexts/html.js +102 -0
- package/node_modules/is-unsafe/src/contexts/log.js +98 -0
- package/node_modules/is-unsafe/src/contexts/nosql.js +114 -0
- package/node_modules/is-unsafe/src/contexts/redos.js +60 -0
- package/node_modules/is-unsafe/src/contexts/shell.js +105 -0
- package/node_modules/is-unsafe/src/contexts/sql-strict.js +47 -0
- package/node_modules/is-unsafe/src/contexts/sql.js +95 -0
- package/node_modules/is-unsafe/src/contexts/svg.js +81 -0
- package/node_modules/is-unsafe/src/contexts/xml.js +79 -0
- package/node_modules/is-unsafe/src/index.cjs +27 -0
- package/node_modules/is-unsafe/src/index.d.ts +95 -0
- package/node_modules/is-unsafe/src/index.js +265 -0
- package/node_modules/path-expression-matcher/README.md +82 -7
- package/node_modules/path-expression-matcher/lib/pem.cjs +1 -1
- package/node_modules/path-expression-matcher/lib/pem.d.cts +94 -3
- package/node_modules/path-expression-matcher/lib/pem.min.js +1 -1
- package/node_modules/path-expression-matcher/lib/pem.min.js.map +1 -1
- package/node_modules/path-expression-matcher/package.json +2 -1
- package/node_modules/path-expression-matcher/src/ExpressionSet.js +18 -2
- package/node_modules/path-expression-matcher/src/Matcher.js +109 -16
- package/node_modules/path-expression-matcher/src/index.d.ts +67 -1
- package/node_modules/path-expression-matcher/src/index.js +3 -2
- package/node_modules/semver/classes/range.js +24 -4
- package/node_modules/semver/classes/semver.js +19 -2
- package/node_modules/semver/package.json +2 -2
- package/node_modules/semver/ranges/subset.js +2 -2
- package/node_modules/strnum/CHANGELOG.md +14 -0
- package/node_modules/strnum/README.md +9 -0
- package/node_modules/strnum/package.json +7 -3
- package/node_modules/strnum/strnum.js +14 -2
- package/node_modules/xml-naming/LICENSE +21 -0
- package/node_modules/xml-naming/README.md +99 -1
- package/node_modules/xml-naming/package.json +4 -2
- package/node_modules/xml-naming/src/index.d.ts +34 -0
- package/node_modules/xml-naming/src/index.js +134 -22
- package/node_modules/yargs/build/index.cjs +1 -1
- package/node_modules/yargs/package.json +6 -4
- package/node_modules/yargs/yargs +5 -9
- package/node_modules/yargs/yargs.cjs +15 -0
- package/package.json +8 -9
- package/node_modules/yargs/browser.d.ts +0 -5
|
@@ -5,6 +5,30 @@
|
|
|
5
5
|
|
|
6
6
|
import { XML as DEFAULT_XML_ENTITIES } from "./entities.js"
|
|
7
7
|
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Entity hook action constants
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Action constants for `onExternalEntity` and `onInputEntity` hooks.
|
|
14
|
+
*
|
|
15
|
+
* Use these instead of raw strings to avoid typos:
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* import EntityDecoder, { ENTITY_ACTION } from './EntityDecoder.js';
|
|
19
|
+
* const dec = new EntityDecoder({
|
|
20
|
+
* onInputEntity: (name, value) => ENTITY_ACTION.BLOCK,
|
|
21
|
+
* });
|
|
22
|
+
*/
|
|
23
|
+
export const ENTITY_ACTION = Object.freeze({
|
|
24
|
+
/** Resolve and expand the entity normally. */
|
|
25
|
+
ALLOW: 'allow',
|
|
26
|
+
/** Silently skip this entity — it will not be registered. */
|
|
27
|
+
BLOCK: 'block',
|
|
28
|
+
/** Throw an error, aborting entity registration entirely. */
|
|
29
|
+
THROW: 'throw',
|
|
30
|
+
});
|
|
31
|
+
|
|
8
32
|
// ---------------------------------------------------------------------------
|
|
9
33
|
// Helpers
|
|
10
34
|
// ---------------------------------------------------------------------------
|
|
@@ -169,6 +193,14 @@ export default class EntityDecoder {
|
|
|
169
193
|
* the effective action is max(onNCR, rangeMinimum).
|
|
170
194
|
* @param {'remove'|'throw'} [options.ncr.nullNCR='remove']
|
|
171
195
|
* Action for U+0000 (null). 'allow' and 'leave' are clamped to 'remove' since null is never safe.
|
|
196
|
+
* @param {((name: string, value: string) => 'allow'|'block'|'throw')|null} [options.onExternalEntity=null]
|
|
197
|
+
* Hook called when an external entity is registered via `setExternalEntities()` or
|
|
198
|
+
* `addExternalEntity()`. Return `ENTITY_ACTION.ALLOW` to accept the entity,
|
|
199
|
+
* `ENTITY_ACTION.BLOCK` to silently skip it, or `ENTITY_ACTION.THROW` to abort with an error.
|
|
200
|
+
* @param {((name: string, value: string) => 'allow'|'block'|'throw')|null} [options.onInputEntity=null]
|
|
201
|
+
* Hook called when an input entity is registered via `addInputEntities()`. Return
|
|
202
|
+
* `ENTITY_ACTION.ALLOW` to accept, `ENTITY_ACTION.BLOCK` to silently skip, or
|
|
203
|
+
* `ENTITY_ACTION.THROW` to abort with an error.
|
|
172
204
|
*/
|
|
173
205
|
constructor(options = {}) {
|
|
174
206
|
this._limit = options.limit || {};
|
|
@@ -204,6 +236,43 @@ export default class EntityDecoder {
|
|
|
204
236
|
this._ncrXmlVersion = ncrCfg.xmlVersion;
|
|
205
237
|
this._ncrOnLevel = ncrCfg.onLevel;
|
|
206
238
|
this._ncrNullLevel = ncrCfg.nullLevel;
|
|
239
|
+
|
|
240
|
+
// --- Registration hooks ---
|
|
241
|
+
/** @type {((name: string, value: string) => 'allow'|'block'|'throw')|null} */
|
|
242
|
+
this._onExternalEntity = typeof options.onExternalEntity === 'function'
|
|
243
|
+
? options.onExternalEntity
|
|
244
|
+
: null;
|
|
245
|
+
/** @type {((name: string, value: string) => 'allow'|'block'|'throw')|null} */
|
|
246
|
+
this._onInputEntity = typeof options.onInputEntity === 'function'
|
|
247
|
+
? options.onInputEntity
|
|
248
|
+
: null;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// -------------------------------------------------------------------------
|
|
252
|
+
// Private: registration hook dispatch
|
|
253
|
+
// -------------------------------------------------------------------------
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Invoke a registration hook for a single entity name/value pair.
|
|
257
|
+
* Returns true when the entity should be accepted, false when it should be
|
|
258
|
+
* silently skipped (BLOCK), and throws when the hook returns THROW.
|
|
259
|
+
*
|
|
260
|
+
* @param {((name: string, value: string) => 'allow'|'block'|'throw')|null} hook
|
|
261
|
+
* @param {string} name
|
|
262
|
+
* @param {string} value
|
|
263
|
+
* @param {string} context — used in error messages ('external' | 'input')
|
|
264
|
+
* @returns {boolean} true = accept, false = skip
|
|
265
|
+
*/
|
|
266
|
+
_applyRegistrationHook(hook, name, value, context) {
|
|
267
|
+
if (!hook) return true; // no hook → always accept
|
|
268
|
+
const action = hook(name, value);
|
|
269
|
+
if (action === ENTITY_ACTION.BLOCK) return false;
|
|
270
|
+
if (action === ENTITY_ACTION.THROW) {
|
|
271
|
+
throw new Error(
|
|
272
|
+
`[EntityDecoder] Registration of ${context} entity "&${name};" was rejected by hook`
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
return true; // ALLOW or any unknown return value → accept
|
|
207
276
|
}
|
|
208
277
|
|
|
209
278
|
// -------------------------------------------------------------------------
|
|
@@ -213,6 +282,9 @@ export default class EntityDecoder {
|
|
|
213
282
|
/**
|
|
214
283
|
* Replace the full set of persistent external entities.
|
|
215
284
|
* All keys are validated — throws on invalid characters.
|
|
285
|
+
* If `onExternalEntity` is set, it is called once per entry; entries that
|
|
286
|
+
* return `ENTITY_ACTION.BLOCK` are silently omitted, `ENTITY_ACTION.THROW`
|
|
287
|
+
* aborts the whole call.
|
|
216
288
|
* @param {Record<string, string | { regex?: RegExp, val: string }>} map
|
|
217
289
|
*/
|
|
218
290
|
setExternalEntities(map) {
|
|
@@ -221,18 +293,34 @@ export default class EntityDecoder {
|
|
|
221
293
|
validateEntityName(key);
|
|
222
294
|
}
|
|
223
295
|
}
|
|
224
|
-
this.
|
|
296
|
+
if (!this._onExternalEntity) {
|
|
297
|
+
this._externalMap = mergeEntityMaps(map);
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
// Hook present — resolve values first, then filter
|
|
301
|
+
const flat = mergeEntityMaps(map);
|
|
302
|
+
const filtered = Object.create(null);
|
|
303
|
+
for (const [name, value] of Object.entries(flat)) {
|
|
304
|
+
if (this._applyRegistrationHook(this._onExternalEntity, name, value, 'external')) {
|
|
305
|
+
filtered[name] = value;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
this._externalMap = filtered;
|
|
225
309
|
}
|
|
226
310
|
|
|
227
311
|
/**
|
|
228
312
|
* Add a single persistent external entity.
|
|
313
|
+
* If `onExternalEntity` is set it is called before the entity is stored;
|
|
314
|
+
* `ENTITY_ACTION.BLOCK` silently skips storage, `ENTITY_ACTION.THROW` raises.
|
|
229
315
|
* @param {string} key
|
|
230
316
|
* @param {string} value
|
|
231
317
|
*/
|
|
232
318
|
addExternalEntity(key, value) {
|
|
233
319
|
validateEntityName(key);
|
|
234
320
|
if (typeof value === 'string' && value.indexOf('&') === -1) {
|
|
235
|
-
this.
|
|
321
|
+
if (this._applyRegistrationHook(this._onExternalEntity, key, value, 'external')) {
|
|
322
|
+
this._externalMap[key] = value;
|
|
323
|
+
}
|
|
236
324
|
}
|
|
237
325
|
}
|
|
238
326
|
|
|
@@ -243,12 +331,25 @@ export default class EntityDecoder {
|
|
|
243
331
|
/**
|
|
244
332
|
* Inject DOCTYPE entities for the current document.
|
|
245
333
|
* Also resets per-document expansion counters.
|
|
334
|
+
* If `onInputEntity` is set it is called once per entry; entries returning
|
|
335
|
+
* `ENTITY_ACTION.BLOCK` are silently omitted, `ENTITY_ACTION.THROW` aborts.
|
|
246
336
|
* @param {Record<string, string | { regx?: RegExp, regex?: RegExp, val: string }>} map
|
|
247
337
|
*/
|
|
248
338
|
addInputEntities(map) {
|
|
249
339
|
this._totalExpansions = 0;
|
|
250
340
|
this._expandedLength = 0;
|
|
251
|
-
this.
|
|
341
|
+
if (!this._onInputEntity) {
|
|
342
|
+
this._inputMap = mergeEntityMaps(map);
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
const flat = mergeEntityMaps(map);
|
|
346
|
+
const filtered = Object.create(null);
|
|
347
|
+
for (const [name, value] of Object.entries(flat)) {
|
|
348
|
+
if (this._applyRegistrationHook(this._onInputEntity, name, value, 'input')) {
|
|
349
|
+
filtered[name] = value;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
this._inputMap = filtered;
|
|
252
353
|
}
|
|
253
354
|
|
|
254
355
|
// -------------------------------------------------------------------------
|
|
@@ -293,7 +394,7 @@ export default class EntityDecoder {
|
|
|
293
394
|
decode(str) {
|
|
294
395
|
if (typeof str !== 'string' || str.length === 0) return str;
|
|
295
396
|
//TODO: check if needed
|
|
296
|
-
|
|
397
|
+
if (str.indexOf('&') === -1) return str; // fast path — no entities at all
|
|
297
398
|
|
|
298
399
|
const original = str;
|
|
299
400
|
const chunks = [];
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
//
|
|
2
|
-
import {
|
|
1
|
+
// EntityEncoder.js
|
|
2
|
+
import { buildTries } from './entityTries.js';
|
|
3
|
+
// NOTE: no static import of ALL_ENTITIES / entities.js here — that import
|
|
4
|
+
// was the actual tree-shaking leak (see constructor comment below).
|
|
3
5
|
|
|
4
6
|
// Replacement strings indexed by char code — direct array access, no hashing
|
|
5
7
|
const XML_UNSAFE_REPLACEMENT = new Array(128);
|
|
@@ -26,6 +28,42 @@ export default class EntityEncoder {
|
|
|
26
28
|
this.encodeAllNamed = options.encodeAllNamed !== false;
|
|
27
29
|
this.maxReplacements = options.maxReplacements || 0;
|
|
28
30
|
this.replacementsCount = 0;
|
|
31
|
+
|
|
32
|
+
// Tries are only built when named-entity encoding is actually requested,
|
|
33
|
+
// and only from what the caller explicitly hands in. There is no
|
|
34
|
+
// built-in default entity set imported here — that static import was
|
|
35
|
+
// exactly what pinned the entire ALL_ENTITIES data (all categories) into
|
|
36
|
+
// every consumer's bundle regardless of what they actually used. A `||`
|
|
37
|
+
// fallback to a statically-imported default does NOT tree-shake: the
|
|
38
|
+
// import is still "used" from the bundler's point of view even if the
|
|
39
|
+
// runtime branch never executes.
|
|
40
|
+
//
|
|
41
|
+
// BREAKING CHANGE from earlier versions: `new EntityEncoder()` with no
|
|
42
|
+
// `namedEntities` and `encodeAllNamed !== false` now throws instead of
|
|
43
|
+
// silently encoding the full built-in set. Callers who want the old
|
|
44
|
+
// "encode everything" behavior must opt in explicitly:
|
|
45
|
+
// import { ALL_ENTITIES } from '@nodable/entities';
|
|
46
|
+
// new EntityEncoder({ namedEntities: ALL_ENTITIES });
|
|
47
|
+
if (this.encodeAllNamed) {
|
|
48
|
+
if (!options.namedEntities) {
|
|
49
|
+
throw new Error(
|
|
50
|
+
'[EntityEncoder] encodeAllNamed is true but no `namedEntities` was ' +
|
|
51
|
+
'provided. Pass a specific set (e.g. { ...COMMON_HTML, ...CURRENCY }) ' +
|
|
52
|
+
'or the full built-in set via `namedEntities: ALL_ENTITIES` imported ' +
|
|
53
|
+
'from \'@nodable/entities\'. This is required so bundlers can ' +
|
|
54
|
+
'tree-shake unused entity categories out of your build; set ' +
|
|
55
|
+
'`encodeAllNamed: false` if you don\'t need named-entity encoding at all.'
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
const tries = buildTries(options.namedEntities);
|
|
59
|
+
this.trie1 = tries.trie1;
|
|
60
|
+
this.trie2 = tries.trie2;
|
|
61
|
+
this.trie3 = tries.trie3;
|
|
62
|
+
} else {
|
|
63
|
+
this.trie1 = null;
|
|
64
|
+
this.trie2 = null;
|
|
65
|
+
this.trie3 = null;
|
|
66
|
+
}
|
|
29
67
|
}
|
|
30
68
|
|
|
31
69
|
encode(str) {
|
|
@@ -38,6 +76,9 @@ export default class EntityEncoder {
|
|
|
38
76
|
// Hoist to locals — avoids `this` property lookup inside the hot loop
|
|
39
77
|
const encodeXmlSafe = this.encodeXmlSafe;
|
|
40
78
|
const encodeAllNamed = this.encodeAllNamed;
|
|
79
|
+
const trie1 = this.trie1;
|
|
80
|
+
const trie2 = this.trie2;
|
|
81
|
+
const trie3 = this.trie3;
|
|
41
82
|
|
|
42
83
|
const len = str.length;
|
|
43
84
|
|
|
@@ -84,32 +125,34 @@ export default class EntityEncoder {
|
|
|
84
125
|
let matchedEntity = null;
|
|
85
126
|
let advance = 1;
|
|
86
127
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
128
|
+
if (encodeAllNamed) {
|
|
129
|
+
// Try 3-char match first (longest wins)
|
|
130
|
+
const mid3 = trie3.get(c0);
|
|
131
|
+
if (mid3 !== undefined) {
|
|
132
|
+
const c1 = str.charCodeAt(i + 1);
|
|
133
|
+
const inner3 = mid3.get(c1);
|
|
134
|
+
if (inner3 !== undefined) {
|
|
135
|
+
const c2 = str.charCodeAt(i + 2);
|
|
136
|
+
const candidate = inner3.get(c2);
|
|
137
|
+
if (candidate !== undefined) { matchedEntity = candidate; advance = 3; }
|
|
138
|
+
}
|
|
96
139
|
}
|
|
97
|
-
}
|
|
98
140
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
141
|
+
// Try 2-char match
|
|
142
|
+
if (matchedEntity === null) {
|
|
143
|
+
const inner2 = trie2.get(c0);
|
|
144
|
+
if (inner2 !== undefined) {
|
|
145
|
+
const c1 = str.charCodeAt(i + 1);
|
|
146
|
+
const candidate = inner2.get(c1);
|
|
147
|
+
if (candidate !== undefined) { matchedEntity = candidate; advance = 2; }
|
|
148
|
+
}
|
|
106
149
|
}
|
|
107
|
-
}
|
|
108
150
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
151
|
+
// Try 1-char match
|
|
152
|
+
if (matchedEntity === null) {
|
|
153
|
+
const candidate = trie1.get(c0);
|
|
154
|
+
if (candidate !== undefined) { matchedEntity = candidate; }
|
|
155
|
+
}
|
|
113
156
|
}
|
|
114
157
|
|
|
115
158
|
if (matchedEntity !== null) {
|
|
@@ -153,18 +196,20 @@ export default class EntityEncoder {
|
|
|
153
196
|
let matchedEntity = null;
|
|
154
197
|
let advance = 1;
|
|
155
198
|
|
|
156
|
-
if (
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
199
|
+
if (encodeAllNamed) {
|
|
200
|
+
if (i + 1 < len) {
|
|
201
|
+
const inner2 = trie2.get(c0);
|
|
202
|
+
if (inner2 !== undefined) {
|
|
203
|
+
const c1 = str.charCodeAt(i + 1);
|
|
204
|
+
const candidate = inner2.get(c1);
|
|
205
|
+
if (candidate !== undefined) { matchedEntity = candidate; advance = 2; }
|
|
206
|
+
}
|
|
162
207
|
}
|
|
163
|
-
}
|
|
164
208
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
209
|
+
if (matchedEntity === null) {
|
|
210
|
+
const candidate = trie1.get(c0);
|
|
211
|
+
if (candidate !== undefined) { matchedEntity = candidate; }
|
|
212
|
+
}
|
|
168
213
|
}
|
|
169
214
|
|
|
170
215
|
if (matchedEntity !== null) {
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// all-entities.js
|
|
2
|
+
//
|
|
3
|
+
// ALL_ENTITIES lives in its OWN module, deliberately separate from
|
|
4
|
+
// entities.js. An object spread like `{ ...GREEK, ...CYRILLIC, ... }` is
|
|
5
|
+
// treated conservatively by bundlers (esbuild, Rollup, webpack) as a
|
|
6
|
+
// potential side effect — spreading can invoke getters or Symbol.iterator —
|
|
7
|
+
// so a module containing such a spread cannot be safely tree-shaken. If that
|
|
8
|
+
// spread lived inside entities.js alongside the individual category exports,
|
|
9
|
+
// its mere presence would pin every category into any bundle that imports
|
|
10
|
+
// even one of them, regardless of whether ALL_ENTITIES itself is ever used.
|
|
11
|
+
//
|
|
12
|
+
// Keeping it isolated here means:
|
|
13
|
+
// - `import { COMMON_HTML } from './entities.js'` tree-shakes cleanly —
|
|
14
|
+
// no other category is dragged in.
|
|
15
|
+
// - Only consumers who explicitly `import { ALL_ENTITIES } from
|
|
16
|
+
// './all-entities.js'` (re-exported from index.js for convenience) pay
|
|
17
|
+
// for the full merged set.
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
BASIC_LATIN,
|
|
21
|
+
LATIN_ACCENTS,
|
|
22
|
+
LATIN_EXTENDED,
|
|
23
|
+
GREEK,
|
|
24
|
+
CYRILLIC,
|
|
25
|
+
MATH,
|
|
26
|
+
MATH_ADVANCED,
|
|
27
|
+
ARROWS,
|
|
28
|
+
SHAPES,
|
|
29
|
+
PUNCTUATION,
|
|
30
|
+
CURRENCY,
|
|
31
|
+
FRACTIONS,
|
|
32
|
+
MISC_SYMBOLS,
|
|
33
|
+
} from './entities.js';
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* All entities combined (if you need everything).
|
|
37
|
+
* @type {Record<string, string>}
|
|
38
|
+
*/
|
|
39
|
+
export const ALL_ENTITIES = {
|
|
40
|
+
...BASIC_LATIN,
|
|
41
|
+
...LATIN_ACCENTS,
|
|
42
|
+
...LATIN_EXTENDED,
|
|
43
|
+
...GREEK,
|
|
44
|
+
...CYRILLIC,
|
|
45
|
+
...MATH,
|
|
46
|
+
...MATH_ADVANCED,
|
|
47
|
+
...ARROWS,
|
|
48
|
+
...SHAPES,
|
|
49
|
+
...PUNCTUATION,
|
|
50
|
+
...CURRENCY,
|
|
51
|
+
...FRACTIONS,
|
|
52
|
+
...MISC_SYMBOLS,
|
|
53
|
+
};
|
|
@@ -34,7 +34,6 @@ export const BASIC_LATIN = {
|
|
|
34
34
|
num: '#',
|
|
35
35
|
dollar: '$',
|
|
36
36
|
percent: '%',
|
|
37
|
-
amp: '&',
|
|
38
37
|
ast: '*',
|
|
39
38
|
commat: '@',
|
|
40
39
|
lowbar: '_',
|
|
@@ -466,9 +465,6 @@ export const CYRILLIC = {
|
|
|
466
465
|
*/
|
|
467
466
|
export const MATH = {
|
|
468
467
|
plus: '+',
|
|
469
|
-
minus: '−',
|
|
470
|
-
mnplus: '∓',
|
|
471
|
-
mp: '∓',
|
|
472
468
|
pm: '±',
|
|
473
469
|
times: '×',
|
|
474
470
|
div: '÷',
|
|
@@ -541,10 +537,6 @@ export const MATH = {
|
|
|
541
537
|
bumpe: '≏',
|
|
542
538
|
bumpeq: '≏',
|
|
543
539
|
HumpEqual: '≏',
|
|
544
|
-
dotminus: '∸',
|
|
545
|
-
minusd: '∸',
|
|
546
|
-
plusdo: '∔',
|
|
547
|
-
dotplus: '∔',
|
|
548
540
|
le: '≤',
|
|
549
541
|
LessEqual: '≤',
|
|
550
542
|
ge: '≥',
|
|
@@ -661,7 +653,6 @@ export const MATH_ADVANCED = {
|
|
|
661
653
|
wreath: '≀',
|
|
662
654
|
nsime: '≄',
|
|
663
655
|
nsimeq: '≄',
|
|
664
|
-
nsimeq: '≄',
|
|
665
656
|
ncong: '≇',
|
|
666
657
|
simne: '≆',
|
|
667
658
|
ncongdot: '⩭̸',
|
|
@@ -778,10 +769,6 @@ export const ARROWS = {
|
|
|
778
769
|
mapsto: '↦',
|
|
779
770
|
mapstodown: '↧',
|
|
780
771
|
crarr: '↵',
|
|
781
|
-
nwarrow: '↖',
|
|
782
|
-
nearrow: '↗',
|
|
783
|
-
searrow: '↘',
|
|
784
|
-
swarrow: '↙',
|
|
785
772
|
nleftarrow: '↚',
|
|
786
773
|
nleftrightarrow: '↮',
|
|
787
774
|
nrightarrow: '↛',
|
|
@@ -822,7 +809,6 @@ export const ARROWS = {
|
|
|
822
809
|
ldrushar: '⥋',
|
|
823
810
|
rdldhar: '⥩',
|
|
824
811
|
lrhard: '⥭',
|
|
825
|
-
rlhar: '⇌',
|
|
826
812
|
uharr: '↾',
|
|
827
813
|
uharl: '↿',
|
|
828
814
|
dharr: '⇂',
|
|
@@ -838,7 +824,6 @@ export const ARROWS = {
|
|
|
838
824
|
nhArr: '⇎',
|
|
839
825
|
nlarr: '↚',
|
|
840
826
|
nlArr: '⇍',
|
|
841
|
-
nrarr: '↛',
|
|
842
827
|
nrArr: '⇏',
|
|
843
828
|
larrb: '⇤',
|
|
844
829
|
LeftArrowBar: '⇤',
|
|
@@ -1000,7 +985,6 @@ export const PUNCTUATION = {
|
|
|
1000
985
|
DiacriticalDot: '˙',
|
|
1001
986
|
DiacriticalDoubleAcute: '˝',
|
|
1002
987
|
grave: '`',
|
|
1003
|
-
acute: '´',
|
|
1004
988
|
};
|
|
1005
989
|
|
|
1006
990
|
/**
|
|
@@ -1014,7 +998,6 @@ export const CURRENCY = {
|
|
|
1014
998
|
yen: '¥',
|
|
1015
999
|
euro: '€',
|
|
1016
1000
|
dollar: '$',
|
|
1017
|
-
euro: '€',
|
|
1018
1001
|
fnof: 'ƒ',
|
|
1019
1002
|
inr: '₹',
|
|
1020
1003
|
af: '؋',
|
|
@@ -1114,7 +1097,6 @@ export const MISC_SYMBOLS = {
|
|
|
1114
1097
|
Vdash: '⊩',
|
|
1115
1098
|
dashv: '⊣',
|
|
1116
1099
|
vDash: '⊨',
|
|
1117
|
-
Vdash: '⊩',
|
|
1118
1100
|
Vvdash: '⊪',
|
|
1119
1101
|
nvdash: '⊬',
|
|
1120
1102
|
nvDash: '⊭',
|
|
@@ -1122,26 +1104,6 @@ export const MISC_SYMBOLS = {
|
|
|
1122
1104
|
nVDash: '⊯',
|
|
1123
1105
|
};
|
|
1124
1106
|
|
|
1125
|
-
/**
|
|
1126
|
-
* All entities combined (if you need everything)
|
|
1127
|
-
* @type {Record<string, string>}
|
|
1128
|
-
*/
|
|
1129
|
-
export const ALL_ENTITIES = {
|
|
1130
|
-
...BASIC_LATIN,
|
|
1131
|
-
...LATIN_ACCENTS,
|
|
1132
|
-
...LATIN_EXTENDED,
|
|
1133
|
-
...GREEK,
|
|
1134
|
-
...CYRILLIC,
|
|
1135
|
-
...MATH,
|
|
1136
|
-
...MATH_ADVANCED,
|
|
1137
|
-
...ARROWS,
|
|
1138
|
-
...SHAPES,
|
|
1139
|
-
...PUNCTUATION,
|
|
1140
|
-
...CURRENCY,
|
|
1141
|
-
...FRACTIONS,
|
|
1142
|
-
...MISC_SYMBOLS,
|
|
1143
|
-
};
|
|
1144
|
-
|
|
1145
1107
|
export const XML = {
|
|
1146
1108
|
amp: "&",
|
|
1147
1109
|
apos: "'",
|
|
@@ -1,49 +1,60 @@
|
|
|
1
1
|
// entityTries.js
|
|
2
|
-
// Builds integer-keyed tries so the
|
|
2
|
+
// Builds integer-keyed tries so the encoder never allocates a string object
|
|
3
3
|
// during lookup — every key is a plain charCode number.
|
|
4
4
|
//
|
|
5
5
|
// trie1: Map<code0, entity>
|
|
6
6
|
// trie2: Map<code0, Map<code1, entity>>
|
|
7
7
|
// trie3: Map<code0, Map<code1, Map<code2, entity>>>
|
|
8
|
+
//
|
|
9
|
+
// Trie construction is now a pure function, called on-demand by whoever
|
|
10
|
+
// needs it (EntityEncoder, at instantiation time) instead of running
|
|
11
|
+
// eagerly at module-import time against the full ALL_ENTITIES set. This
|
|
12
|
+
// keeps the cost — and the entity data itself — out of any bundle/consumer
|
|
13
|
+
// that never actually asks for it.
|
|
8
14
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
export const trie2 = new Map(); // code0 → Map → entity string
|
|
19
|
-
export const trie3 = new Map(); // code0 → Map → Map → entity string
|
|
15
|
+
/**
|
|
16
|
+
* Build integer-keyed tries from a name → chars entity map.
|
|
17
|
+
* @param {Record<string, string>} entityMap
|
|
18
|
+
* @returns {{ trie1: Map, trie2: Map, trie3: Map }}
|
|
19
|
+
*/
|
|
20
|
+
export function buildTries(entityMap) {
|
|
21
|
+
const trie1 = new Map(); // code0 → entity string
|
|
22
|
+
const trie2 = new Map(); // code0 → Map → entity string
|
|
23
|
+
const trie3 = new Map(); // code0 → Map → Map → entity string
|
|
20
24
|
|
|
21
|
-
|
|
22
|
-
|
|
25
|
+
// Reverse map: character sequence → "&name;"
|
|
26
|
+
// (built inline, not kept around — only the tries need to survive)
|
|
27
|
+
for (const name in entityMap) {
|
|
28
|
+
const chars = entityMap[name];
|
|
29
|
+
const entity = `&${name};`;
|
|
30
|
+
const len = chars.length;
|
|
23
31
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
if (len === 1) {
|
|
33
|
+
const c0 = chars.charCodeAt(0);
|
|
34
|
+
// Keep shortest match only if no longer match already claimed this code
|
|
35
|
+
// (longer matches are inserted in the same pass so we just overwrite —
|
|
36
|
+
// trie1 is only consulted after trie2/trie3 both miss, so no conflict)
|
|
37
|
+
trie1.set(c0, entity);
|
|
30
38
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
} else if (len === 2) {
|
|
40
|
+
const c0 = chars.charCodeAt(0);
|
|
41
|
+
const c1 = chars.charCodeAt(1);
|
|
42
|
+
let inner = trie2.get(c0);
|
|
43
|
+
if (inner === undefined) { inner = new Map(); trie2.set(c0, inner); }
|
|
44
|
+
inner.set(c1, entity);
|
|
37
45
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
} else if (len === 3) {
|
|
47
|
+
const c0 = chars.charCodeAt(0);
|
|
48
|
+
const c1 = chars.charCodeAt(1);
|
|
49
|
+
const c2 = chars.charCodeAt(2);
|
|
50
|
+
let mid = trie3.get(c0);
|
|
51
|
+
if (mid === undefined) { mid = new Map(); trie3.set(c0, mid); }
|
|
52
|
+
let inner = mid.get(c1);
|
|
53
|
+
if (inner === undefined) { inner = new Map(); mid.set(c1, inner); }
|
|
54
|
+
inner.set(c2, entity);
|
|
55
|
+
}
|
|
56
|
+
// HTML5 has no named entity whose character sequence is longer than 3 chars
|
|
47
57
|
}
|
|
48
|
-
|
|
49
|
-
}
|
|
58
|
+
|
|
59
|
+
return { trie1, trie2, trie3 };
|
|
60
|
+
}
|