From 48a07d7421895537095c140f766b3adadd21b755 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 5 Oct 2026 19:08:27 +0900 Subject: [PATCH 1/2] feat(lexer): add opt-in minified enumerable detection Minifiers can replace `enumerable: true` with `enumerable: !0`, which prevents detection of otherwise supported exports and star reexports. `allowMinifiedEnumerable` enables this spelling for each parse call and preserves the default detection patterns. Refs: https://github.com/nodejs/cjs-module-lexer/issues/64 --- README.md | 18 +- build/Makefile | 1 + include-wasm/cjs-module-lexer.h | 1 + lexer.d.ts | 6 +- lexer.js | 43 +- lib/lexer.wasm | Bin 22419 -> 22596 bytes lib/lexer.wat | 16757 +++++++++++++++--------------- src/lexer.c | 30 +- src/lexer.js | 6 +- test/_unit.js | 88 +- 10 files changed, 8278 insertions(+), 8672 deletions(-) diff --git a/README.md b/README.md index d1343d8..80d6418 100755 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ This project is used in Node.js core for detecting the named exports available w PRs will be accepted and upstreamed for parser bugs, performance improvements or new syntax support only. -_Detection patterns for this project are **frozen**_. This is because adding any new export detection patterns would result in fragmented backwards-compatibility. Specifically, it would be very difficult to figure out why an ES module named export for CommonJS might work in newer Node.js versions but not older versions. This problem would only be discovered downstream of module authors, with the fix for module authors being to then have to understand which patterns in this project provide full backwards-compatibily. Rather, by fully freezing the detected patterns, if it works in any Node.js version it will work in any other. Build tools can also reliably treat the supported syntax for this project as a part of their output target for ensuring syntax support. +_Default detection patterns for this project are **frozen**_. This is because adding any new export detection patterns would result in fragmented backwards-compatibility. Specifically, it would be very difficult to figure out why an ES module named export for CommonJS might work in newer Node.js versions but not older versions. This problem would only be discovered downstream of module authors, with the fix for module authors being to then have to understand which patterns in this project provide full backwards-compatibily. Rather, by fully freezing the detected patterns, if it works in any Node.js version it will work in any other. Build tools can also reliably treat the supported syntax for this project as a part of their output target for ensuring syntax support. ### Usage @@ -71,9 +71,23 @@ Browser main threads can restrict synchronous Wasm compilation, so await The Wasm build is around 1.5x faster and without a cold start. +The optional third argument to `parse(source, name, options)` controls detection extensions. +`allowMinifiedEnumerable` defaults to `false`. Set it to `true` to recognize `enumerable: !0` +in supported `Object.defineProperty` exports and star reexports. Whitespace and comments can separate `!` and `0`. +This option does not change Node.js's built-in CommonJS named-export detection. + +```js +const { exports } = parse( + "Object.defineProperty(exports, 'b', { enumerable: !0, value: 'b' });", + undefined, + { allowMinifiedEnumerable: true } +); +// exports === ['b'] +``` + ### Grammar -CommonJS exports matches are run against the source token stream. +CommonJS exports matches are run against the source token stream. The grammar below describes default detection. The token grammar is: diff --git a/build/Makefile b/build/Makefile index b8e96d8..e12fd95 100755 --- a/build/Makefile +++ b/build/Makefile @@ -8,6 +8,7 @@ WASM_TARGET := -target wasm32-unknown-wasi WASM_EXTRA_CFLAGS := -I include-wasm/ -Wno-logical-op-parentheses -Wno-parentheses -Oz WASM_EXTRA_LDFLAGS := -Wl,-z,stack-size=13312,--no-entry,--compress-relocations,--strip-all WASM_EXTRA_LDFLAGS += -Wl,--export=__heap_base,--export=parseCJS,--export=sa +WASM_EXTRA_LDFLAGS += -Wl,--export=parseCJSWithOptions WASM_EXTRA_LDFLAGS += -Wl,--export=e,--export=re,--export=es,--export=ee WASM_EXTRA_LDFLAGS += -Wl,--export=rre,--export=ree,--export=res,--export=ru,--export=us,--export=ue diff --git a/include-wasm/cjs-module-lexer.h b/include-wasm/cjs-module-lexer.h index ab2adca..58bb470 100755 --- a/include-wasm/cjs-module-lexer.h +++ b/include-wasm/cjs-module-lexer.h @@ -162,6 +162,7 @@ void (*addReexport)(const uint16_t*, const uint16_t*) = &_addReexport; void (*addUnsafeGetter)(const uint16_t*, const uint16_t*) = &_addUnsafeGetter; void (*clearReexports)() = &_clearReexports; uint32_t parseCJS (uint16_t* source, uint32_t sourceLen, void (*addExport)(const uint16_t* start, const uint16_t* end), void (*addReexport)(const uint16_t* start, const uint16_t* end), void (*addUnsafeGetter)(const uint16_t*, const uint16_t*), void (*clearReexports)()); +uint32_t parseCJSWithOptions (uint16_t* source, uint32_t sourceLen, void (*addExport)(const uint16_t* start, const uint16_t* end), void (*addReexport)(const uint16_t* start, const uint16_t* end), void (*addUnsafeGetter)(const uint16_t*, const uint16_t*), void (*clearReexports)(), uint32_t options); enum RequireType { Import, diff --git a/lexer.d.ts b/lexer.d.ts index cda07d8..6e4ef16 100755 --- a/lexer.d.ts +++ b/lexer.d.ts @@ -3,6 +3,10 @@ export interface Exports { reexports: string[]; } -export declare function parse(source: string, name?: string): Exports; +export interface ParseOptions { + allowMinifiedEnumerable?: boolean; +} + +export declare function parse(source: string, name?: string, options?: ParseOptions): Exports; export declare function init(): Promise; export declare function initSync(): void; diff --git a/lexer.js b/lexer.js index 3e30b6d..54422b8 100755 --- a/lexer.js +++ b/lexer.js @@ -9,6 +9,7 @@ let openTokenDepth, lastSlashWasDivision, templateStackDepth, nextBraceIsClass, + allowMinifiedEnumerable, starExportMap, lastStarExportSpecifier, _exports, @@ -22,6 +23,7 @@ function resetState () { lastSlashWasDivision = false; templateStackDepth = 0; nextBraceIsClass = false; + allowMinifiedEnumerable = false; starExportMap = Object.create(null); lastStarExportSpecifier = null; @@ -49,15 +51,22 @@ function pushTemplate () { templateDepth = ++openTokenDepth; } -function parseCJS (source, name = '@') { +/** + * @param {string} source + * @param {string} [name] + * @param {import('./lexer').ParseOptions} [options] + */ +function parseCJS (source, name = '@', options) { + const minifiedEnumerable = options ? options.allowMinifiedEnumerable === true : false; resetState(); + allowMinifiedEnumerable = minifiedEnumerable; try { parseSource(source); } - catch (e) { - e.message += `\n at ${name}:${source.slice(0, pos).split('\n').length}:${pos - source.lastIndexOf('\n', pos - 1)}`; - e.loc = pos; - throw e; + catch (error) { + error.message += `\n at ${name}:${source.slice(0, pos).split('\n').length}:${pos - source.lastIndexOf('\n', pos - 1)}`; + error.loc = pos; + throw error; } const result = { exports: [..._exports].filter(expt => expt !== undefined && !unsafeGetters.has(expt)), reexports: [...reexports].filter(reexpt => reexpt !== undefined) }; resetState(); @@ -353,6 +362,16 @@ function tryParseObjectHasOwnProperty (it_id) { return true; } +/** @param {number} ch */ +function tryParseMinifiedEnumerable (ch) { + if (!allowMinifiedEnumerable || ch !== 33/*!*/) return false; + pos++; + if (commentWhitespace() !== 48/*0*/) return false; + pos++; + return true; +} + +/** @param {boolean} keys */ function tryParseObjectDefineOrKeys (keys) { pos += 6; let revertPos = pos - 1; @@ -392,8 +411,11 @@ function tryParseObjectDefineOrKeys (keys) { if (ch !== 58/*:*/) break; pos++; ch = commentWhitespace(); - if (ch !== 116/*t*/ || !source.startsWith('rue', pos + 1)) break; - pos += 4; + if (ch === 116/*t*/ && source.startsWith('rue', pos + 1)) { + pos += 4; + } else if (!tryParseMinifiedEnumerable(ch)) { + break; + } ch = commentWhitespace(); if (ch !== 44) break; pos++; @@ -771,8 +793,11 @@ function tryParseObjectDefineOrKeys (keys) { if (ch !== 58/*:*/) break; pos++; ch = commentWhitespace(); - if (ch !== 116/*t*/ && !source.startsWith('rue', pos + 1)) break; - pos += 4; + if (ch === 116/*t*/ || source.startsWith('rue', pos + 1)) { + pos += 4; + } else if (!tryParseMinifiedEnumerable(ch)) { + break; + } ch = commentWhitespace(); if (ch !== 44/*,*/) break; pos++; diff --git a/lib/lexer.wasm b/lib/lexer.wasm index dc291380c4735361e2b391d5795f8db0ea8131fd..88f130abb89f8ee5e9bab58c39dd41a9c5f1aab8 100755 GIT binary patch delta 5879 zcmbVQTWlOx89rw&yKB#^J!|jUj_vi%j4!brC%bmyTB@iVUrn z(%5l8+`6t>MQMdl%8{xH@sI}|5Ku@JB>GT}n z%+?8Ji3v=hhJAYWiZ+QJF8SzB0O!}>B_N2>T)V58c}6Ksoi za)FI$%L{C)zKrKKZTTe2CypOmKC!Uxz!6qxk6v%OW99Zk$5-yS>$4}=cvCZHf?EdT zn%;cjA-31HfhF{7t(Li26{~BY zLcl{ zG-h2B8+|zw@09nCWD{Ub-&}rwWRPp}`H}rw%wn>`HJuoK?2HA1#-ezGiHl6U#l+i8 zyu+M$F`1zya^L7?o|2W(ZTz(S#%Q)PB*(<6Q!-|S?X=jsB~Hs{NBwk*%`FjgS}af% z%;Or&A&o8X+{P`u@?sM90#}HrblX-=Vtt@eVG#<^a6t^0lbLXIf<~3`I2rHUSw4*K zudXrt&jdRx+x@E)8YDk6t^(h3K^lR;WwS z9qg399qWe?-X9z0ru4Ud{IIecEcRz6-eux-CjO%A))ttyjrh`k$O?`&5g~s$DFMY< z!z}!XIVUBb-r8SvU?stwCNd=n4hYb!Ww(WndhZHHr2>X2Qldp%f@-s{OcU+k$y6gF zd(kJmIVt5EB?F|xdY3!>Me;^9PVP%t0ZE5Q8c?mARIf2*BK4X8qj0pj{%SkT#iS!1 zgXVmSG201+{v#8ABm0OyMd)e`IJ5#5UV|~@>)TrUjsIjPvLMjhm-l(66#2}Q0gD1N zTBD+&MV+ow_xR}pWH-TX2 zmvrDYF|`r#Km%V_@QMuW<0NVdT8Kzw*mq$<^%{e1LQuft38D-DxVqC_OzKu$11CaM z4Kw!^j%-RP8C>pa;+r|BkiuCZ>=^Q`LIHxiDUVsM^k%!D$Qu*AW(qsU6Y6llV4kUD4iSK9B1T`~2nFZxj6S-(^Y)Z@72c{%B9v$Y{*|Evqs*kbBdud(PnTDG-5G#!&59ov{G^wlc{hfQ{3LWi zlEZfLxAOSz$(``YU6c7XNNxs8MJTyEH`$5FrzQsn>Rg!1fwzHhEdz)Hia?K;+=}Jb z?mUakR&H}4t~DoCgup?a8B(Gcu4r^f(V z$?dT#2w%Iry~@k+j+X!(o_dq@Lf~h0uA8d~hnW zfp?+}>%8;7(1smf*t;zaujuNm)lb7VDP}1=7OwHrFjzEQGZ;+=c++9-G5TC5U;x^1 zTZ2ZyQt0%7yk{aag1wZx)H;MxI9gkDkR~I3wKPc#ZcKT~0TgqYvS^GlBxRo{HS&6D zRIfp5tW`s`+!&+WXsFy6DthlsJ=r}wqmo?|_Up+mc6qWh-55d61f?+9K~9zIf?uU& zA`AjL1%X{x8oL2z!KsVPw$86G*PwIdk&f0j1itD-C*rttXDM+n&92XP_>Sc3rGqt% zJhD-8l~2rO=fYg}Lsi>&T&%j(ZxEDSPIgbINCto4H?c?#)GCPUAnBS2{)iyb71=7a zH5PI>Ryk-+Z0bi`1Wxg-u;2)))nHjGrqCahaiT5s25Vp?cm)-_-4e`{l1;U71mSvZ zkA>RGV!gr#yAMCqmxO#=^b{gOzD64!2>Ke(rl@~v4ZW2b0jGfC12R50Sk)8(mmOM8 zT0-|UJ3%qVvYV+Tq3RX}91Y5!Jd}29+qRq}$$e&xX;s1~JwuVGf`f&SD4_aqB@iw? zzl1LjX;PTi3np-IR>b&H&oVk4&DG>1b3-{z{FBk14_LU#m|wCmt^Tm)?YYt|Y7JbX zi9Gd�AwF_jIriTA#o?X%->8W9 z6<72+S^4|@Q!Tk*B341!TTxq+Tc%X&{%=sFN-^QLh@2aw$(#^;FhZEX!-&pBj5>28M&i0(3gY! z*a{&*4b}m*Y_JLav?f1SdWN^iedQrM7t3xnfROmthdRYC1K=?0u)I1bp_jNwu!j;6 z4hacmf;#XaSa}&z;OX-o)Op;4DT~P_RHI29))E>x!O-##r5-tOU3u_S2xU4mz;Z~1 z4iL9ghYhh#$urk=^#u}#@Q)Vq@CcojKflhqQ8nRIpsA}1D!XkDR?(c6Kxoe(EgvTN zJxxUdx_Joi8-Od9S`y7Q=3V9)5J0u=a_@Y0Qak8!kI@j}kgg~=U0)bLjIIc&!-13k z9!~7)V>-?yAoR8Q?mgtUA|j4<{j$R4ORPlja@r{SGjX^0GDxki8uLAhs?+Z2s09eL z7v{U#b#acQEnzKgICg3_#_a<>E;I562FKFqEubF{&p}!-Kn0G*{7nVw3<_vVAOK?9 z>VZ!W7qh-cu&V=(b}hLC4~D6{8M_XH7)zvcgVZ76@>mv z+Hm*!I$yPKJ&mT5NvEWPQk9o`aJ+Xey`{m8?`;s<*fY?oFbDHIw@zdNp z|M|qD3GT{~)KB9c@)C1gc_~%mt#YdEXnSiA>b}T%6*Ioiop$+1+e|aw02%_Qe7Eg2 zDtV(8ENpS*#rAHv$4VEtBlo7OA1Q+bSwnc_cH9hZWjO5{8Br8A0tN*}h;3ccxdB4Z z@yWD53dV{AazV)Ws&_|D<{k)y?iA1OWQImj@kLvsitlRIPXEYE-+WcPM&&rJP&U%0 zR`_T*-gM#VGxQt*XPoz-IG11V*tdyvapP`+z`@BAxZ6aqbXw(`9Y^4gxz4Syb$-;@v)yV|7qZXg9QFD_PP{~~4g!aZBuOCww_PX%8wOC)8Rx%pu*3d0 Db|R5U delta 5541 zcmb7IYiwjy9Y5#XJCDx2Gd*o*rqj;M&b@c~n0C8u7q;!v3flv;bwRSoeh?FsU6P{d z1ZBJI2MKYOxY?{xUO6#=h!LU@qaWPOnnI!r8CTCENMHbB*R~tiS<9*P2Tsd-qsDddC>3;+8f=;B=R}Yqpnau|Q z50CB`yxGb*bJ`!;qx*(#<5J%})aaKH2A|8qLRQ3YgzKrHp-CAqHN5086)YXe#?(9} zx6&YH!PfJ7cSjMJFCDHGtZf-QGJN@*^vx0Y_Jx=Swn8W&ZCH{>&Mt;KTf~zE{l@U+ zJfn9v@(BZ{Wv+lL1$|TFdT#5VHu{SY!*;ntSHZVYj00)@*%$>tapdPbd-j=;I_JKA zZ}ddgafKsYzWyDk|TEw(RLLTVSh{QMlVH+bM;peC zV48C?Ja_gxbJxja9ktbT}_!{CVNWk0F{WT9>ryt-W{FHv2 z&wJ$OY7JKBUPAHPRsx$gPOdS<<`9=BbX{CIL}y|VhGNQI@tFnp3%C+Z&pu!_1vWjd z?-IRJ*j@=N;1Khh|9RZA_uKQH1p(o#9nC-xKJH)dVA z(_Y9HJxeC=;}+m-psrqL>Mf?;Wa@3E-eD3dED(tPrZo(^K5mUqg@~fKTn~mOcycX= zL<>1ME=i@X%$i+2WC!^yg+NlmzYC5Ef(>AfEcO~omr2b^G0S(`t{B*^r6v}*&sdt%rjaVX; zb{mzxnJo7qWG>*=^;Eq@&Z)Rew^K)kcy!$E$XZ*(1L`g#9)7@YPMw1k^NIljZis9b zNgA8XT?xVr>2peW(Kg_bFMMPQN3~L9t1c%*DG8+wA(pacnKark%QOf@yP5XVl^qLN zsU8JMKFgRZBc}hx)IW@I{vNwnXGozFKHzW2(0eHiOs0Z$GAqOd@4r8c=d83}yO>W7C4mrCP6G1dU#A&m0?J?ZmZXwEE?K#HYtMyAps4q^{+ z_gcq6xXLU+3e?;OYbZ-y2odAB`ufZyU?xj_DXCs29QxW)Fa=+ak;&N9fsOEih1ac} zwLF^=tc7xv@R*~Yc6%Z2NLyTB-tPoexK>y@SX>I|T4WJ@%JCH{UY5JIiKf`cqHGKe ztru*Iv`Xh&U{I%3Zy^Idd`9s*r{`TNB{jE#=f~W>a9sv-$P1g)fDGfY1?0KA+^No( z%UFRBXwP~>kuXe0lwl!4wSgDPx{>WnB?AQMGiDXZQ)P>{ASy#5B5ZyD6*+>1r0sLc zP$2-`BXpb5V4L#68%&uHP_LSd`v#M+S&Ujn4gmfuF#%s}=lu zuxBH^8^0tQUncuwrjw*gPc#Et22gCG8Ke|H=BMf0OX3Oc6-+tKpyGZQMw@W&v#9P$s_?u^4<0J%f9Z?hm+s$A;zJFP2dSHwLx9!wz@td z-VdRYaWXA5LanIEi5DLibfIfVOtTuC>1iMI8k(uX}oSDPjqdg zUJNJ@@9Ql!q$wo^2!&$3KE=e^^$FN7Ur#PnjZJZgE0o=?P-Yocs4&ALLq{1lWD=Cm z_eNz1>#ItQ8Dxe?jmQj9uMs4-7NCTeBo|3U1xPhWWMFu#L~4RGc)nE%WpO)02;Sg7lk)?%Wj^#^{g?WHpgwff9096!rSS{%VUx2M6^OP!a=x%N*22s{Y@N zs(;kC8Zb;ZAS9O%bdG7l@yeSRG2C2uvY72y--j`1_&9AC1faNWMSXn@-xH`G1iAC) zts_eBEte-xu7PJD3OHRj74`O>K#!$PtYLmJI|N`bp3+|_4^DG~z+(qivyQTS+f7p% z$haBm@~MY}0!PCxWCB7<$8|C8>OYrvKq>n~ImTLy-jST#W-6=6gl#C==LZ<3hwF4Vu6dWBK_4SVx`j387q z!vRQ^%_~k{X0peJDKKORbQ_`tgo%)B_*_S=FRot8EBZvWUe8lsLM;KJk?@~mvW&?d zlaHC!sh88gsaCnruT(#k?{PcG)OJ(7Zy3U)P}T=`%nnq;gJcuUf~*>45)?*?5G?@n z`pk|4ixoFT7}8C|F_6d}H|c;+G?(B3I)4?{vj}7w?Qj)9=t16Qvfl+i1OuG1suye1 zdl0%bIWo_N4*MZQE*y$|0dI0lzeVsbFj63%K%Lj8YXdu~5D>V)AJ9XyKwl0onGyEw z+LJu1AM=LreA)9xLXT4aJ8vx^PulR6MB7U@`lz1pE7K7)+;d66GigR-ORFG5QOMw) zN1je$NwXcok|W-zQ=-^WXvJM?zgb!)E6}7*$}!WqDtMR& zJ$zWZ+5;@OGo!J7G^kEti~EeWKs)>HL>Tl$Cf!Lc4R{lL$o2\02\00\00\03\00\00\00\09\00\00\00\09\00\00\00r\01\00\00\01\00\00\00\9a\00\00\00\0a\00\00\00\b0\00\00\00\02\00\00\006\00\00\00\0e\00\00\00 \00\00\00\09\00\00\00\10\00\00\00\03\00\00\00.\00\00\00\0a\00\00\006\00\00\00\09\00\00\00\07\00\00\00\02\00\00\00%\00\00\00\0d\00\00\00\02\00\00\00\09\00\00\00\06\00\00\00\01\00\00\00-\00\00\00\00\00\00\00\0d\00\00\00\02\00\00\001\00\00\00\0d\00\00\00\09\00\00\00\03\00\00\00\02\00\00\00\0b\00\00\00S\00\00\00\0b\00\00\00\07\00\00\00\00\00\00\00\a1\00\00\00\0b\00\00\00\06\00\00\00\09\00\00\00\07\00\00\00\03\00\00\008\00\00\00\01\00\00\00\02\00\00\00\06\00\00\00\03\00\00\00\01\00\00\00\03\00\00\00\02\00\00\00\0a\00\00\00\00\00\00\00\0b\00\00\00\01\00\00\00\03\00\00\00\06\00\00\00\04\00\00\00\04\00\00\00\c1\00\00\00\11\00\00\00\0a\00\00\00\09\00\00\00\05\00\00\00\00\00\00\00R\00\00\00\13\00\00\00\0d\00\00\00\09\00\00\00\d6\00\00\00\06\00\00\00\03\00\00\00\08\00\00\00\1c\00\00\00\01\00\00\00S\00\00\00\10\00\00\00\10\00\00\00\09\00\00\00R\00\00\00\0c\00\00\00\09\00\00\00\09\00\00\00T\00\00\00\0e\00\00\00\05\00\00\00\09\00\00\00\f3\00\00\00\0e\00\00\00\a6\00\00\00\09\00\00\00G\00\00\00\05\00\00\00\02\00\00\00\01\00\00\00\03\00\00\00\03\00\00\00\02\00\00\00\00\00\00\00\02\00\00\00\01\00\00\00\0d\00\00\00\09\00\00\00x\00\00\00\06\00\00\00\03\00\00\00\06\00\00\00\04\00\00\00\00\00\00\00\1d\00\00\00\09\00\00\00)\00\00\00\06\00\00\00\02\00\00\00\03\00\00\00\09\00\00\00\00\00\00\00\0a\00\00\00\0a\00\00\00/\00\00\00\0f\00\00\00\96\01\00\00\07\00\00\00\02\00\00\00\07\00\00\00\11\00\00\00\09\00\00\009\00\00\00\15\00\00\00\02\00\00\00\0d\00\00\00{\00\00\00\05\00\00\00\04\00\00\00\00\00\00\00\02\00\00\00\01\00\00\00\02\00\00\00\06\00\00\00\02\00\00\00\00\00\00\00\09\00\00\00\09\00\00\001\00\00\00\04\00\00\00\02\00\00\00\01\00\00\00\02\00\00\00\04\00\00\00\09\00\00\00\09\00\00\00J\01\00\00\03\00\00\00jK\00\00\09\00\00\00\87\00\00\00\04\00\00\00<\00\00\00\06\00\00\00\1a\00\00\00\09\00\00\00\f6\03\00\00\00\00\00\00\02\00\00\006\00\00\00\08\00\00\00\03\00\00\00R\00\00\00\00\00\00\00\0c\00\00\00\01\00\00\00\acL\00\00\01\00\00\00\c7\14\00\00\04\00\00\00\04\00\00\00\05\00\00\00\09\00\00\00\07\00\00\00\03\00\00\00\06\00\00\00\1f\00\00\00\03\00\00\00\95\00\00\00\02\00\00\00\8a\05\00\001\00\00\00\01\02\00\006\00\00\00\05\00\00\001\00\00\00\09\00\00\00\00\00\00\00\0f\00\00\00\00\00\00\00\17\00\00\00\04\00\00\00\02\00\00\00\0e\00\00\00Q\05\00\00\06\00\00\00\02\00\00\00\10\00\00\00\03\00\00\00\06\00\00\00\02\00\00\00\01\00\00\00\02\00\00\00\04\00\00\00\06\01\00\00\06\00\00\00\0a\00\00\00\09\00\00\00\a3\01\00\00\0d\00\00\00\d7\05\00\00\06\00\00\00n\00\00\00\06\00\00\00\06\00\00\00\09\00\00\00\97\12\00\00\09\00\00\00\07\05\0c\00\ef\00\00\00") - (data (;1;) (i32.const 3992) "P\8c\00\00\01\00\00\00\02\00\00\00\03\00\00\00\04\00\00\00\00\04\00\00\f0\1f\00\00")) + (export "__heap_base" (global 0)) + (export "sa" (func 37)) + (export "e" (func 38)) + (export "es" (func 39)) + (export "ee" (func 40)) + (export "res" (func 41)) + (export "ree" (func 42)) + (export "us" (func 43)) + (export "ue" (func 44)) + (export "re" (func 45)) + (export "rre" (func 46)) + (export "ru" (func 47)) + (export "parseCJS" (func 48)) + (export "parseCJSWithOptions" (func 49)) + (elem (;0;) (i32.const 1) func 0 1 2 3) + (data (;0;) (i32.const 1044) "\0b\00\00\00\02\00\00\00\19\00\00\00\02\00\00\00\12\00\00\00\02\00\00\00\01\00\00\00\02\00\00\00\0e\00\00\00\03\00\00\00\0d\00\00\00#\00\00\00z\00\00\00F\00\00\004\00\00\00\0c\01\00\00\1c\00\00\00\04\00\00\000\00\00\000\00\00\00\1f\00\00\00\0e\00\00\00\1d\00\00\00\06\00\00\00%\00\00\00\0b\00\00\00\1d\00\00\00\03\00\00\00#\00\00\00\05\00\00\00\07\00\00\00\02\00\00\00\04\00\00\00+\00\00\00\9d\00\00\00\13\00\00\00#\00\00\00\05\00\00\00#\00\00\00\05\00\00\00'\00\00\00\09\00\00\003\00\00\00\9d\00\00\006\01\00\00\0a\00\00\00\15\00\00\00\0b\00\00\00\07\00\00\00\99\00\00\00\05\00\00\00\03\00\00\00\00\00\00\00\02\00\00\00+\00\00\00\02\00\00\00\01\00\00\00\04\00\00\00\00\00\00\00\03\00\00\00\16\00\00\00\0b\00\00\00\16\00\00\00\0a\00\00\00\1e\00\00\00B\00\00\00\12\00\00\00\02\00\00\00\01\00\00\00\0b\00\00\00\15\00\00\00\0b\00\00\00\19\00\00\00G\00\00\007\00\00\00\07\00\00\00\01\00\00\00A\00\00\00\00\00\00\00\10\00\00\00\03\00\00\00\02\00\00\00\02\00\00\00\02\00\00\00\1c\00\00\00+\00\00\00\1c\00\00\00\04\00\00\00\1c\00\00\00$\00\00\00\07\00\00\00\02\00\00\00\1b\00\00\00\1c\00\00\005\00\00\00\0b\00\00\00\15\00\00\00\0b\00\00\00\12\00\00\00\0e\00\00\00\11\00\00\00o\00\00\00H\00\00\008\00\00\002\00\00\00\0e\00\00\002\00\00\00\0e\00\00\00#\00\00\00]\01\00\00)\00\00\00\07\00\00\00\01\00\00\00O\00\00\00\1c\00\00\00\0b\00\00\00\00\00\00\00\09\00\00\00\15\00\00\00k\00\00\00\14\00\00\00\1c\00\00\00\16\00\00\00\0d\00\00\004\00\00\00L\00\00\00,\00\00\00!\00\00\00\18\00\00\00\1b\00\00\00#\00\00\00\1e\00\00\00\00\00\00\00\03\00\00\00\00\00\00\00\09\00\00\00\22\00\00\00\04\00\00\00\00\00\00\00\0d\00\00\00/\00\00\00\0f\00\00\00\03\00\00\00\16\00\00\00\00\00\00\00\02\00\00\00\00\00\00\00$\00\00\00\11\00\00\00\02\00\00\00\18\00\00\00U\00\00\00\06\00\00\00\02\00\00\00\00\00\00\00\02\00\00\00\03\00\00\00\02\00\00\00\0e\00\00\00\02\00\00\00\09\00\00\00\08\00\00\00.\00\00\00'\00\00\00\07\00\00\00\03\00\00\00\01\00\00\00\03\00\00\00\15\00\00\00\02\00\00\00\06\00\00\00\02\00\00\00\01\00\00\00\02\00\00\00\04\00\00\00\04\00\00\00\00\00\00\00\13\00\00\00\00\00\00\00\0d\00\00\00\04\00\00\00\9f\00\00\004\00\00\00\13\00\00\00\03\00\00\00\15\00\00\00\02\00\00\00\1f\00\00\00/\00\00\00\15\00\00\00\01\00\00\00\02\00\00\00\00\00\00\00\b9\00\00\00.\00\00\00*\00\00\00\03\00\00\00%\00\00\00/\00\00\00\15\00\00\00\00\00\00\00<\00\00\00*\00\00\00\0e\00\00\00\00\00\00\00H\00\00\00\1a\00\00\00\e6\00\00\00+\00\00\00u\00\00\00?\00\00\00 \00\00\00\07\00\00\00\03\00\00\00\00\00\00\00\03\00\00\00\07\00\00\00\02\00\00\00\01\00\00\00\02\00\00\00\17\00\00\00\10\00\00\00\00\00\00\00\02\00\00\00\00\00\00\00_\00\00\00\07\00\00\00\03\00\00\00&\00\00\00\11\00\00\00\00\00\00\00\02\00\00\00\00\00\00\00\1d\00\00\00\00\00\00\00\0b\00\00\00'\00\00\00\08\00\00\00\00\00\00\00\16\00\00\00\00\00\00\00\0c\00\00\00-\00\00\00\14\00\00\00\00\00\00\00#\00\00\008\00\00\00\08\01\00\00\08\00\00\00\02\00\00\00$\00\00\00\12\00\00\00\00\00\00\002\00\00\00\1d\00\00\00q\00\00\00\06\00\00\00\02\00\00\00\01\00\00\00\02\00\00\00%\00\00\00\16\00\00\00\00\00\00\00\1a\00\00\00\05\00\00\00\02\00\00\00\01\00\00\00\02\00\00\00\1f\00\00\00\0f\00\00\00\00\00\00\00H\01\00\00\12\00\00\00\be\00\00\00\00\00\00\00P\00\00\00\99\03\00\00g\00\00\00n\00\00\00\12\00\00\00\c3\00\00\00\bd\0a\00\00.\04\00\00\d2\0f\00\00F\02\00\00\ba!\00\008\02\00\00\08\00\00\00\1e\00\00\00r\00\00\00\1d\00\00\00\13\00\00\00/\00\00\00\11\00\00\00\03\00\00\00 \00\00\00\14\00\00\00\06\00\00\00\12\00\00\00\b1\02\00\00?\00\00\00\81\00\00\00J\00\00\00\06\00\00\00\00\00\00\00C\00\00\00\0c\00\00\00A\00\00\00\01\00\00\00\02\00\00\00\00\00\00\00\1d\00\00\00\f7\17\00\00\09\00\00\00\d5\04\00\00+\00\00\00\08\00\00\00\f8\22\00\00\1e\01\00\002\00\00\00\02\00\00\00\12\00\00\00\03\00\00\00\09\00\00\00\8b\01\00\00\05\09\00\00j\00\00\00\06\00\00\00\0c\00\00\00\04\00\00\00\08\00\00\00\08\00\00\00\09\00\00\00g\17\00\00T\00\00\00\02\00\00\00F\00\00\00\02\00\00\00\01\00\00\00\03\00\00\00\00\00\00\00\03\00\00\00\01\00\00\00\03\00\00\00\03\00\00\00\02\00\00\00\0b\00\00\00\02\00\00\00\00\00\00\00\02\00\00\00\06\00\00\00\02\00\00\00@\00\00\00\02\00\00\00\03\00\00\00\03\00\00\00\07\00\00\00\02\00\00\00\06\00\00\00\02\00\00\00\1b\00\00\00\02\00\00\00\03\00\00\00\02\00\00\00\04\00\00\00\02\00\00\00\00\00\00\00\04\00\00\00\06\00\00\00\02\00\00\00S\01\00\00\03\00\00\00\18\00\00\00\02\00\00\00\18\00\00\00\02\00\00\00\1e\00\00\00\02\00\00\00\18\00\00\00\02\00\00\00\1e\00\00\00\02\00\00\00\18\00\00\00\02\00\00\00\1e\00\00\00\02\00\00\00\18\00\00\00\02\00\00\00\1e\00\00\00\02\00\00\00\18\00\00\00\02\00\00\00\07\00\00\005\09\00\00,\00\00\00\0b\00\00\00\06\00\00\00\11\00\00\00\00\00\00\00r\01\00\00+\00\00\00\15\05\00\00\c4\00\00\00<\00\00\00C\00\00\00\08\00\00\00\00\00\00\00\b5\04\00\00\03\00\00\00\02\00\00\00\1a\00\00\00\02\00\00\00\01\00\00\00\02\00\00\00\00\00\00\00\03\00\00\00\00\00\00\00\02\00\00\00\09\00\00\00\02\00\00\00\03\00\00\00\02\00\00\00\00\00\00\00\02\00\00\00\00\00\00\00\07\00\00\00\00\00\00\00\05\00\00\00\00\00\00\00\02\00\00\00\00\00\00\00\02\00\00\00\00\00\00\00\02\00\00\00\02\00\00\00\02\00\00\00\01\00\00\00\02\00\00\00\00\00\00\00\03\00\00\00\00\00\00\00\02\00\00\00\00\00\00\00\02\00\00\00\00\00\00\00\02\00\00\00\00\00\00\00\02\00\00\00\00\00\00\00\02\00\00\00\01\00\00\00\02\00\00\00\00\00\00\00\03\00\00\00\03\00\00\00\02\00\00\00\06\00\00\00\02\00\00\00\03\00\00\00\02\00\00\00\03\00\00\00\02\00\00\00\00\00\00\00\02\00\00\00\09\00\00\00\02\00\00\00\10\00\00\00\06\00\00\00\02\00\00\00\02\00\00\00\04\00\00\00\02\00\00\00\10\00\00\00E\11\00\00\dd\a6\00\00#\00\00\004\10\00\00\0c\00\00\00\dd\00\00\00\03\00\00\00\81\16\00\00\0f\00\00\000\1d\00\00 \0c\00\00\1d\02\00\00\e3\05\00\00J\13\00\00\fd\01\00\00\00\00\00\00\e3\00\00\00\00\00\00\00\96\00\00\00\04\00\00\00&\01\00\00\09\00\00\00X\05\00\00\02\00\00\00\02\00\00\00\01\00\00\00\06\00\00\00\03\00\00\00)\00\00\00\02\00\00\00\05\00\00\00\00\00\00\00\a6\00\00\00\01\00\00\00>\02\00\00\03\00\00\00\09\00\00\00\09\00\00\00r\01\00\00\01\00\00\00\9a\00\00\00\0a\00\00\00\b0\00\00\00\02\00\00\006\00\00\00\0e\00\00\00 \00\00\00\09\00\00\00\10\00\00\00\03\00\00\00.\00\00\00\0a\00\00\006\00\00\00\09\00\00\00\07\00\00\00\02\00\00\00%\00\00\00\0d\00\00\00\02\00\00\00\09\00\00\00\06\00\00\00\01\00\00\00-\00\00\00\00\00\00\00\0d\00\00\00\02\00\00\001\00\00\00\0d\00\00\00\09\00\00\00\03\00\00\00\02\00\00\00\0b\00\00\00S\00\00\00\0b\00\00\00\07\00\00\00\00\00\00\00\a1\00\00\00\0b\00\00\00\06\00\00\00\09\00\00\00\07\00\00\00\03\00\00\008\00\00\00\01\00\00\00\02\00\00\00\06\00\00\00\03\00\00\00\01\00\00\00\03\00\00\00\02\00\00\00\0a\00\00\00\00\00\00\00\0b\00\00\00\01\00\00\00\03\00\00\00\06\00\00\00\04\00\00\00\04\00\00\00\c1\00\00\00\11\00\00\00\0a\00\00\00\09\00\00\00\05\00\00\00\00\00\00\00R\00\00\00\13\00\00\00\0d\00\00\00\09\00\00\00\d6\00\00\00\06\00\00\00\03\00\00\00\08\00\00\00\1c\00\00\00\01\00\00\00S\00\00\00\10\00\00\00\10\00\00\00\09\00\00\00R\00\00\00\0c\00\00\00\09\00\00\00\09\00\00\00T\00\00\00\0e\00\00\00\05\00\00\00\09\00\00\00\f3\00\00\00\0e\00\00\00\a6\00\00\00\09\00\00\00G\00\00\00\05\00\00\00\02\00\00\00\01\00\00\00\03\00\00\00\03\00\00\00\02\00\00\00\00\00\00\00\02\00\00\00\01\00\00\00\0d\00\00\00\09\00\00\00x\00\00\00\06\00\00\00\03\00\00\00\06\00\00\00\04\00\00\00\00\00\00\00\1d\00\00\00\09\00\00\00)\00\00\00\06\00\00\00\02\00\00\00\03\00\00\00\09\00\00\00\00\00\00\00\0a\00\00\00\0a\00\00\00/\00\00\00\0f\00\00\00\96\01\00\00\07\00\00\00\02\00\00\00\07\00\00\00\11\00\00\00\09\00\00\009\00\00\00\15\00\00\00\02\00\00\00\0d\00\00\00{\00\00\00\05\00\00\00\04\00\00\00\00\00\00\00\02\00\00\00\01\00\00\00\02\00\00\00\06\00\00\00\02\00\00\00\00\00\00\00\09\00\00\00\09\00\00\001\00\00\00\04\00\00\00\02\00\00\00\01\00\00\00\02\00\00\00\04\00\00\00\09\00\00\00\09\00\00\00J\01\00\00\03\00\00\00jK\00\00\09\00\00\00\87\00\00\00\04\00\00\00<\00\00\00\06\00\00\00\1a\00\00\00\09\00\00\00\f6\03\00\00\00\00\00\00\02\00\00\006\00\00\00\08\00\00\00\03\00\00\00R\00\00\00\00\00\00\00\0c\00\00\00\01\00\00\00\acL\00\00\01\00\00\00\c7\14\00\00\04\00\00\00\04\00\00\00\05\00\00\00\09\00\00\00\07\00\00\00\03\00\00\00\06\00\00\00\1f\00\00\00\03\00\00\00\95\00\00\00\02\00\00\00\8a\05\00\001\00\00\00\01\02\00\006\00\00\00\05\00\00\001\00\00\00\09\00\00\00\00\00\00\00\0f\00\00\00\00\00\00\00\17\00\00\00\04\00\00\00\02\00\00\00\0e\00\00\00Q\05\00\00\06\00\00\00\02\00\00\00\10\00\00\00\03\00\00\00\06\00\00\00\02\00\00\00\01\00\00\00\02\00\00\00\04\00\00\00\06\01\00\00\06\00\00\00\0a\00\00\00\09\00\00\00\a3\01\00\00\0d\00\00\00\d7\05\00\00\06\00\00\00n\00\00\00\06\00\00\00\06\00\00\00\09\00\00\00\97\12\00\00\09\00\00\00\07\05\0c\00\ef") + (data (;1;) (i32.const 3992) "P\8c\00\00\01\00\00\00\02\00\00\00\03\00\00\00\04\00\00\00\00\04\00\00\f0\1f")) diff --git a/src/lexer.c b/src/lexer.c index fd3f1cf..21c1efd 100755 --- a/src/lexer.c +++ b/src/lexer.c @@ -22,6 +22,7 @@ uint16_t* templateStack; uint16_t** openTokenPosStack; StarExportBinding* starExportStack; bool nextBraceIsClass; +bool allowMinifiedEnumerable; uint16_t* lastReexportStart; uint16_t* lastReexportEnd; @@ -68,10 +69,15 @@ static inline __attribute__((always_inline)) bool pushTemplate () { return true; } -// Note: parsing is based on the _assumption_ that the source is already valid uint32_t parseCJS (uint16_t* _source, uint32_t _sourceLen, void (*_addExport)(const uint16_t*, const uint16_t*), void (*_addReexport)(const uint16_t*, const uint16_t*), void (*_addUnsafeGetter)(const uint16_t*, const uint16_t*), void (*_clearReexports)()) { + return parseCJSWithOptions(_source, _sourceLen, _addExport, _addReexport, _addUnsafeGetter, _clearReexports, 0); +} + +// Note: parsing is based on the _assumption_ that the source is already valid +uint32_t parseCJSWithOptions (uint16_t* _source, uint32_t _sourceLen, void (*_addExport)(const uint16_t*, const uint16_t*), void (*_addReexport)(const uint16_t*, const uint16_t*), void (*_addUnsafeGetter)(const uint16_t*, const uint16_t*), void (*_clearReexports)(), uint32_t options) { source = _source; sourceLen = _sourceLen; + allowMinifiedEnumerable = (options & 1) != 0; if (_addExport) addExport = _addExport; if (_addReexport) @@ -352,6 +358,14 @@ bool tryParseObjectHasOwnProperty (uint16_t* it_id_start, ptrdiff_t it_id_len) { return true; } +static inline bool tryParseMinifiedEnumerable (uint16_t ch) { + if (!allowMinifiedEnumerable || ch != '!') return false; + pos++; + if (commentWhitespace() != '0') return false; + pos++; + return true; +} + void tryParseObjectDefineOrKeys (bool keys) { pos += 6; uint16_t* revertPos = pos - 1; @@ -392,8 +406,11 @@ void tryParseObjectDefineOrKeys (bool keys) { if (ch != ':') break; pos++; ch = commentWhitespace(); - if (ch != 't' || !str_eq3(pos + 1, 'r', 'u', 'e')) break; - pos += 4; + if (ch == 't' && str_eq3(pos + 1, 'r', 'u', 'e')) { + pos += 4; + } else if (!tryParseMinifiedEnumerable(ch)) { + break; + } ch = commentWhitespace(); if (ch != 44) break; pos++; @@ -771,8 +788,11 @@ void tryParseObjectDefineOrKeys (bool keys) { if (ch != ':') break; pos++; ch = commentWhitespace(); - if (ch != 't' && !str_eq3(pos + 1, 'r', 'u', 'e')) break; - pos += 4; + if (ch == 't' || str_eq3(pos + 1, 'r', 'u', 'e')) { + pos += 4; + } else if (!tryParseMinifiedEnumerable(ch)) { + break; + } ch = commentWhitespace(); if (ch != ',') break; pos++; diff --git a/src/lexer.js b/src/lexer.js index 431cb5b..13e8592 100755 --- a/src/lexer.js +++ b/src/lexer.js @@ -6,8 +6,10 @@ const hasBuffer = typeof Buffer !== 'undefined'; /** * @param {string} source * @param {string} [name] + * @param {import('../lexer').ParseOptions} [options] */ -export function parse (source, name = '@') { +export function parse (source, name = '@', options) { + const flags = options && options.allowMinifiedEnumerable === true ? 1 : 0; if (!wasm) initSync(); @@ -28,7 +30,7 @@ export function parse (source, name = '@') { else (isLE ? copyLE : copyBE)(source, new Uint16Array(memory, addr, len)); - const err_code = wasm.parseCJS(addr, source.length, 0, 0, 0); + const err_code = wasm.parseCJSWithOptions(addr, source.length, 0, 0, 0, 0, flags); if (err_code) { const err = new Error(`Parse error ${name}${wasm.e()}:${source.slice(0, wasm.e()).split('\n').length}:${wasm.e() - source.lastIndexOf('\n', wasm.e() - 1)}`); diff --git a/test/_unit.js b/test/_unit.js index 719d922..25384ae 100755 --- a/test/_unit.js +++ b/test/_unit.js @@ -1,4 +1,4 @@ -const assert = require('assert'); +const assert = require('node:assert/strict'); let parse; async function loadParser () { @@ -88,6 +88,92 @@ suite('Lexer', () => { } }); + test('minified enumerable values and getters require an option', () => { + for (const enumerable of ['!0', '! 0', '!/*comment*/0', '!//comment\n0', '!\u00a00', '!0/*comment*/']) { + const source = ` + Object.defineProperty(exports, 'literal', { enumerable: true, value: 1 }); + Object.defineProperty(exports, 'value', { enumerable: ${enumerable}, value: 2 }); + Object.defineProperty(module.exports, 'getter', { enumerable: ${enumerable}, get () { return value; } }); + Object.defineProperty(exports, 'member', { + enumerable: ${enumerable}, get: function () { return external.value; } + }); + `; + + assert.deepStrictEqual(parse(source), { exports: ['literal'], reexports: [] }); + assert.deepStrictEqual(parse(source, undefined, { allowMinifiedEnumerable: false }), { + exports: ['literal'], reexports: [] + }); + assert.deepStrictEqual(parse(source, undefined, { allowMinifiedEnumerable: true }), { + exports: ['literal', 'value', 'getter', 'member'], reexports: [] + }); + assert.deepStrictEqual(parse(source), { exports: ['literal'], reexports: [] }); + } + }); + + test('minified enumerable reexports require an option', () => { + for (const enumerable of ['!0', '! /*comment*/ 0', '!1', '!0.1', '!0n', '!00', '!0 + 1', 'tfoo', 'xrue']) { + const legacy = enumerable === 'tfoo' || enumerable === 'xrue'; + const minified = enumerable === '!0' || enumerable === '! /*comment*/ 0'; + const source = ` + var external = require('external'); + Object.keys(external).forEach(function (key) { + if (key === 'default' || key === '__esModule') return; + Object.defineProperty(exports, key, { + enumerable: ${enumerable}, get: function () { return external[key]; } + }); + }); + `; + + assert.deepStrictEqual(parse(source), { exports: [], reexports: legacy ? ['external'] : [] }); + assert.deepStrictEqual(parse(source, undefined, { allowMinifiedEnumerable: true }), { + exports: [], reexports: legacy || minified ? ['external'] : [] + }); + assert.deepStrictEqual(parse(source), { exports: [], reexports: legacy ? ['external'] : [] }); + } + }); + + test('minified enumerable detection preserves unsupported descriptors', () => { + for (const enumerable of ['!1', '!0.1', '!0n', '!00', '!0 + 1', '!0 || true', '!!0']) { + const source = `Object.defineProperty(exports, 'value', { enumerable: ${enumerable}, value: 1 });`; + assert.deepStrictEqual(parse(source, undefined, { allowMinifiedEnumerable: true }), { + exports: [], reexports: [] + }); + } + + const source = ` + Object.defineProperty(exports, 'value', { enumerable: !0, get () { return dynamic(); } }); + exports.value = 1; + `; + assert.deepStrictEqual(parse(source, undefined, { allowMinifiedEnumerable: true }), { + exports: [], reexports: [] + }); + assert.throws(() => parse("import 'external';", 'broken.js', { allowMinifiedEnumerable: true }), { + code: 'ERR_LEXER_ESM_SYNTAX', message: /broken\.js/ + }); + assert.deepStrictEqual(parse("Object.defineProperty(exports, 'value', { enumerable: !0, value: 1 });"), { + exports: [], reexports: [] + }); + }); + + test('minified enumerable option getters can reenter the parser', () => { + const source = "Object.defineProperty(exports, 'outer', { enumerable: !0, value: 1 });"; + for (const allowed of [false, true]) { + for (const nested of ['exports.inner = 1', '('.repeat(2049)]) { + const options = { + get allowMinifiedEnumerable () { + if (nested.startsWith('(')) { + assert.throws(() => parse(nested, 'nested.js'), /nested\.js/); + } else { + parse(nested); + } + return allowed; + } + }; + assert.deepStrictEqual(parse(source, undefined, options), { exports: allowed ? ['outer'] : [], reexports: [] }); + } + } + }); + test('automatic Wasm initialization', async () => { const lexer = await import('../dist/lexer.mjs?automatic-initialization'); From 8891efd7a143eadfc3bac9187387b73d18829297 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 11 Oct 2026 12:22:58 +0900 Subject: [PATCH 2/2] fix(lexer): require exact true for enumerable reexports Identifiers such as tfoo and xrue produce false star reexports. The enumerable check accepts either the leading t or the remaining rue. --- lexer.js | 2 +- lib/lexer.wasm | Bin 22596 -> 22591 bytes lib/lexer.wat | 43 ++++++++++++++++++++----------------------- src/lexer.c | 2 +- test/_unit.js | 17 ++++++++++------- 5 files changed, 32 insertions(+), 32 deletions(-) diff --git a/lexer.js b/lexer.js index 54422b8..ebcacd8 100755 --- a/lexer.js +++ b/lexer.js @@ -793,7 +793,7 @@ function tryParseObjectDefineOrKeys (keys) { if (ch !== 58/*:*/) break; pos++; ch = commentWhitespace(); - if (ch === 116/*t*/ || source.startsWith('rue', pos + 1)) { + if (ch === 116/*t*/ && source.startsWith('rue', pos + 1)) { pos += 4; } else if (!tryParseMinifiedEnumerable(ch)) { break; diff --git a/lib/lexer.wasm b/lib/lexer.wasm index 88f130abb89f8ee5e9bab58c39dd41a9c5f1aab8..4917a0ed79b1ff82d734ffbaea05213b3cf62a32 100755 GIT binary patch delta 41 xcmX@IfpPx^#tkNnjCUrRGCDK;cH3OVC@IFl { } }); - test('minified enumerable reexports require an option', () => { - for (const enumerable of ['!0', '! /*comment*/ 0', '!1', '!0.1', '!0n', '!00', '!0 + 1', 'tfoo', 'xrue']) { - const legacy = enumerable === 'tfoo' || enumerable === 'xrue'; + test('enumerable reexports require true or an enabled minified value', () => { + for (const enumerable of ['true', '!0', '! /*comment*/ 0', '!1', '!0.1', '!0n', '!00', '!0 + 1', 'tfoo', 'xrue', 'truex']) { + const literal = enumerable === 'true'; const minified = enumerable === '!0' || enumerable === '! /*comment*/ 0'; const source = ` var external = require('external'); @@ -124,16 +124,19 @@ suite('Lexer', () => { }); `; - assert.deepStrictEqual(parse(source), { exports: [], reexports: legacy ? ['external'] : [] }); + assert.deepStrictEqual(parse(source), { exports: [], reexports: literal ? ['external'] : [] }); + assert.deepStrictEqual(parse(source, undefined, { allowMinifiedEnumerable: false }), { + exports: [], reexports: literal ? ['external'] : [] + }); assert.deepStrictEqual(parse(source, undefined, { allowMinifiedEnumerable: true }), { - exports: [], reexports: legacy || minified ? ['external'] : [] + exports: [], reexports: literal || minified ? ['external'] : [] }); - assert.deepStrictEqual(parse(source), { exports: [], reexports: legacy ? ['external'] : [] }); + assert.deepStrictEqual(parse(source), { exports: [], reexports: literal ? ['external'] : [] }); } }); test('minified enumerable detection preserves unsupported descriptors', () => { - for (const enumerable of ['!1', '!0.1', '!0n', '!00', '!0 + 1', '!0 || true', '!!0']) { + for (const enumerable of ['!1', '!0.1', '!0n', '!00', '!0 + 1', '!0 || true', '!!0', 'tfoo', 'xrue', 'truex']) { const source = `Object.defineProperty(exports, 'value', { enumerable: ${enumerable}, value: 1 });`; assert.deepStrictEqual(parse(source, undefined, { allowMinifiedEnumerable: true }), { exports: [], reexports: []