MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

support string literal export/import names

+86 -26
+30 -26
src/ant.c
··· 22913 22913 return val; 22914 22914 } 22915 22915 22916 + static bool esm_parse_name(struct js *js, const char **name, size_t *len, bool *is_string) { 22917 + if (next(js) == TOK_STRING) { 22918 + jsval_t sv = js_str_literal(js); 22919 + jsoff_t slen; jsoff_t soff = vstr(js, sv, &slen); 22920 + *name = (const char *)&js->mem[soff]; *len = slen; 22921 + if (is_string) *is_string = true; 22922 + } else if (next(js) == TOK_IDENTIFIER || next(js) == TOK_DEFAULT) { 22923 + *name = &js->code[js->toff]; 22924 + *len = js->tlen; 22925 + if (is_string) *is_string = false; 22926 + } else return false; 22927 + 22928 + js->consumed = 1; 22929 + return true; 22930 + } 22931 + 22916 22932 static int esm_parse_named_imports(struct js *js, esm_import_binding_t *bindings, int max_bindings) { 22917 22933 int count = 0; 22918 22934 22919 22935 while (next(js) != TOK_RBRACE && count < max_bindings) { 22920 - if (next(js) != TOK_IDENTIFIER && next(js) != TOK_DEFAULT) { 22936 + bool import_is_string = false; 22937 + const char *import_name; 22938 + size_t import_len; 22939 + 22940 + if (!esm_parse_name(js, &import_name, &import_len, &import_is_string)) 22921 22941 return -1; 22922 - } 22923 - const char *import_name = &js->code[js->toff]; 22924 - size_t import_len = js->tlen; 22925 - js->consumed = 1; 22926 22942 22927 22943 const char *local_name = import_name; 22928 22944 size_t local_len = import_len; 22929 22945 22930 22946 if (next(js) == TOK_AS) { 22931 22947 js->consumed = 1; 22932 - if (next(js) != TOK_IDENTIFIER && next(js) != TOK_DEFAULT) { 22948 + if (!esm_parse_name(js, &local_name, &local_len, NULL)) 22933 22949 return -1; 22934 - } 22935 - local_name = &js->code[js->toff]; 22936 - local_len = js->tlen; 22937 - js->consumed = 1; 22950 + } else if (import_is_string) { 22951 + return -1; 22938 22952 } 22939 22953 22940 22954 bindings[count].import_name = import_name; ··· 23317 23331 23318 23332 if (next(js) == TOK_AS) { 23319 23333 js->consumed = 1; 23320 - EXPECT(TOK_IDENTIFIER); 23321 - alias_name = &js->code[js->toff]; 23322 - alias_len = js->tlen; 23323 - js->consumed = 1; 23334 + if (!esm_parse_name(js, &alias_name, &alias_len, NULL)) 23335 + return js_mkerr_typed(js, JS_ERR_SYNTAX, "expected identifier or string after 'as'"); 23324 23336 } 23325 23337 23326 23338 EXPECT(TOK_FROM); ··· 23364 23376 while (next(js) != TOK_RBRACE) { 23365 23377 if (spec_count >= 64) return js_mkerr(js, "too many export specifiers"); 23366 23378 23367 - if (next(js) != TOK_IDENTIFIER && next(js) != TOK_DEFAULT) { 23368 - return js_mkerr_typed(js, JS_ERR_SYNTAX, "expected identifier or 'default' in export list"); 23369 - } 23370 - specs[spec_count].local = &js->code[js->toff]; 23371 - specs[spec_count].local_len = js->tlen; 23379 + if (!esm_parse_name(js, &specs[spec_count].local, &specs[spec_count].local_len, NULL)) 23380 + return js_mkerr_typed(js, JS_ERR_SYNTAX, "expected identifier, string, or 'default' in export list"); 23372 23381 specs[spec_count].exported = specs[spec_count].local; 23373 23382 specs[spec_count].export_len = specs[spec_count].local_len; 23374 - js->consumed = 1; 23375 23383 23376 23384 if (next(js) == TOK_AS) { 23377 23385 js->consumed = 1; 23378 - if (next(js) != TOK_IDENTIFIER && next(js) != TOK_DEFAULT) { 23379 - return js_mkerr_typed(js, JS_ERR_SYNTAX, "expected identifier or 'default' after 'as'"); 23380 - } 23381 - specs[spec_count].exported = &js->code[js->toff]; 23382 - specs[spec_count].export_len = js->tlen; 23383 - js->consumed = 1; 23386 + if (!esm_parse_name(js, &specs[spec_count].exported, &specs[spec_count].export_len, NULL)) 23387 + return js_mkerr_typed(js, JS_ERR_SYNTAX, "expected identifier, string, or 'default' after 'as'"); 23384 23388 } 23385 23389 23386 23390 spec_count++;
+9
tests/string-export-module.js
··· 1 + const jq = "jquery"; 2 + const foo = 42; 3 + const bar = "hello"; 4 + const baz = true; 5 + 6 + export { jq as "matrix" }; 7 + export { foo as "foo-bar" }; 8 + export { bar as "unicode \u0041" }; 9 + export { baz as "default" };
+1
tests/string-local-reexport-module.js
··· 1 + export { "matrix" as "renamed" } from './string-export-module.js';
+1
tests/string-reexport-module.js
··· 1 + export * as "nested" from './string-export-module.js';
+45
tests/test_string_exports.js
··· 1 + // Test: String literal export/import names (ES2022) 2 + // Tests export { x as "string" }, import { "string" as x }, export * as "string" 3 + 4 + console.log("=== Testing string literal export/import names ===\n"); 5 + 6 + let passed = 0; 7 + let failed = 0; 8 + 9 + function assert(condition, msg) { 10 + if (condition) { 11 + console.log("โœ“", msg); 12 + passed++; 13 + } else { 14 + console.log("โœ—", msg); 15 + failed++; 16 + } 17 + } 18 + 19 + // Test 1: import { "string" as local } from module 20 + async function main() { 21 + // Basic string-named exports 22 + const mod = await import('./string-export-module.js'); 23 + 24 + assert(mod["matrix"] === "jquery", 'export { jq as "matrix" }'); 25 + assert(mod["foo-bar"] === 42, 'export { foo as "foo-bar" }'); 26 + assert(mod["unicode A"] === "hello", 'export { bar as "unicode \\u0041" }'); 27 + assert(mod["default"] === true, 'export { baz as "default" }'); 28 + 29 + // Test 2: export * as "string" from re-export 30 + const remod = await import('./string-reexport-module.js'); 31 + 32 + assert(typeof remod["nested"] === "object", 'export * as "nested" produces object'); 33 + assert(remod["nested"]["matrix"] === "jquery", 're-exported "matrix" accessible via "nested"'); 34 + assert(remod["nested"]["foo-bar"] === 42, 're-exported "foo-bar" accessible via "nested"'); 35 + 36 + // Test 3: string as local name in export (valid only in re-exports) 37 + const remod2 = await import('./string-local-reexport-module.js'); 38 + 39 + assert(remod2["renamed"] === "jquery", 'export { "matrix" as "renamed" } from re-export'); 40 + 41 + console.log(`\n=== Results: ${passed} passed, ${failed} failed ===`); 42 + if (failed > 0) process.exit(1); 43 + } 44 + 45 + void main();