@knighted/module 1.3.0-rc.0 → 1.3.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.
@@ -510,7 +510,7 @@ const format = async (src, ast, opts) => {
510
510
  ok: false,
511
511
  reason: 'multiple-writes'
512
512
  };
513
- if (!isValidExportName(entry.key)) return {
513
+ if (entry.key !== 'default' && !isValidExportName(entry.key)) return {
514
514
  ok: false,
515
515
  reason: 'non-identifier-key'
516
516
  };
@@ -527,6 +527,39 @@ const format = async (src, ast, opts) => {
527
527
  const raw = code.slice(node.start, node.end);
528
528
  return raw.replace(/\b__dirname\b/g, 'import.meta.dirname').replace(/\b__filename\b/g, 'import.meta.filename');
529
529
  };
530
+ const tryObjectLiteralExport = (rhs, baseIsModuleExports, propName) => {
531
+ if (!baseIsModuleExports || propName !== 'exports') return null;
532
+ if (rhs.type !== 'ObjectExpression') return null;
533
+ const exportsOut = [];
534
+ const seenKeys = new Set();
535
+ for (const prop of rhs.properties) {
536
+ if (prop.type !== 'Property') return null;
537
+ if (prop.kind !== 'init') return null;
538
+ if (prop.computed || prop.method) return null;
539
+ if (prop.key.type !== 'Identifier') return null;
540
+ const key = prop.key.name;
541
+ if (key === '__proto__' || key === 'prototype') return null;
542
+ if (!isValidExportName(key)) return null;
543
+ if (seenKeys.has(key)) return null;
544
+ const value = prop.value.type === 'Identifier' && prop.shorthand ? prop.key : prop.value;
545
+ if (!isAllowedRhs(value)) return null;
546
+ if (expressionHasRequireCall(value, requireShadowed)) return null;
547
+ const rhsSrc = rhsSourceFor(value);
548
+ if (value.type === 'Identifier' && value.name === key) {
549
+ exportsOut.push(`export { ${key} };`);
550
+ } else if (value.type === 'Identifier') {
551
+ exportsOut.push(`export { ${rhsSrc} as ${key} };`);
552
+ } else {
553
+ exportsOut.push(`export const ${key} = ${rhsSrc};`);
554
+ }
555
+ seenKeys.add(key);
556
+ }
557
+ exportsOut.push(`export default ${rhsSourceFor(rhs)};`);
558
+ return {
559
+ exportsOut,
560
+ seenKeys
561
+ };
562
+ };
530
563
  for (const entry of entries) {
531
564
  const write = entry.writes[0];
532
565
  if (write.type !== 'AssignmentExpression') {
@@ -545,7 +578,7 @@ const format = async (src, ast, opts) => {
545
578
  const base = left.object;
546
579
  const propName = left.property.name;
547
580
  const baseIsExports = base.type === 'Identifier' && base.name === 'exports';
548
- const baseIsModuleExports = base.type === 'MemberExpression' && base.object.type === 'Identifier' && base.object.name === 'module' && base.property.type === 'Identifier' && base.property.name === 'exports';
581
+ const baseIsModuleExports = base.type === 'Identifier' && base.name === 'module' && propName === 'exports' || base.type === 'MemberExpression' && base.object.type === 'Identifier' && base.object.name === 'module' && base.property.type === 'Identifier' && base.property.name === 'exports';
549
582
  if (!baseIsExports && !baseIsModuleExports) {
550
583
  return {
551
584
  ok: false,
@@ -553,25 +586,35 @@ const format = async (src, ast, opts) => {
553
586
  };
554
587
  }
555
588
  const rhs = write.right;
556
- if (!isAllowedRhs(rhs)) return {
557
- ok: false,
558
- reason: 'unsupported-rhs'
559
- };
560
- if (expressionHasRequireCall(rhs, requireShadowed)) {
561
- return {
589
+ const objectLiteralPlan = tryObjectLiteralExport(rhs, baseIsModuleExports, propName);
590
+ if (!objectLiteralPlan) {
591
+ if (!isAllowedRhs(rhs)) return {
562
592
  ok: false,
563
- reason: 'rhs-require'
593
+ reason: 'unsupported-rhs'
564
594
  };
595
+ if (expressionHasRequireCall(rhs, requireShadowed)) {
596
+ return {
597
+ ok: false,
598
+ reason: 'rhs-require'
599
+ };
600
+ }
565
601
  }
566
602
  const rhsSrc = rhsSourceFor(rhs);
567
603
  if (propName === 'exports' && baseIsModuleExports) {
568
- // module.exports = ... handles default
569
- if (seen.has('default')) return {
570
- ok: false,
571
- reason: 'duplicate-default'
572
- };
573
- seen.add('default');
574
- exportsOut.push(`export default ${rhsSrc};`);
604
+ if (objectLiteralPlan) {
605
+ for (const line of objectLiteralPlan.exportsOut) {
606
+ exportsOut.push(line);
607
+ }
608
+ objectLiteralPlan.seenKeys.forEach(k => seen.add(k));
609
+ } else {
610
+ // module.exports = ... handles default
611
+ if (seen.has('default')) return {
612
+ ok: false,
613
+ reason: 'duplicate-default'
614
+ };
615
+ seen.add('default');
616
+ exportsOut.push(`export default ${rhsSrc};`);
617
+ }
575
618
  } else {
576
619
  if (seen.has(propName)) return {
577
620
  ok: false,
@@ -831,11 +874,14 @@ const format = async (src, ast, opts) => {
831
874
  code.overwrite(rep.start, rep.end, idiomaticPlan.exports[idx]);
832
875
  });
833
876
  } else {
834
- for (const rep of idiomaticPlan.replacements) {
835
- code.overwrite(rep.start, rep.end, ';');
877
+ const [first, ...rest] = idiomaticPlan.replacements;
878
+ if (first) {
879
+ code.overwrite(first.start, first.end, idiomaticPlan.exports.join('\n'));
836
880
  }
837
- if (idiomaticPlan.exports.length) {
838
- code.append(`\n${idiomaticPlan.exports.join('\n')}\n`);
881
+ for (const rep of rest) {
882
+ const original = code.slice(rep.start, rep.end);
883
+ const hasSemicolon = original.trimEnd().endsWith(';');
884
+ code.overwrite(rep.start, rep.end, hasSemicolon ? ';' : '');
839
885
  }
840
886
  }
841
887
  }
package/dist/format.js CHANGED
@@ -503,7 +503,7 @@ const format = async (src, ast, opts) => {
503
503
  ok: false,
504
504
  reason: 'multiple-writes'
505
505
  };
506
- if (!isValidExportName(entry.key)) return {
506
+ if (entry.key !== 'default' && !isValidExportName(entry.key)) return {
507
507
  ok: false,
508
508
  reason: 'non-identifier-key'
509
509
  };
@@ -520,6 +520,39 @@ const format = async (src, ast, opts) => {
520
520
  const raw = code.slice(node.start, node.end);
521
521
  return raw.replace(/\b__dirname\b/g, 'import.meta.dirname').replace(/\b__filename\b/g, 'import.meta.filename');
522
522
  };
523
+ const tryObjectLiteralExport = (rhs, baseIsModuleExports, propName) => {
524
+ if (!baseIsModuleExports || propName !== 'exports') return null;
525
+ if (rhs.type !== 'ObjectExpression') return null;
526
+ const exportsOut = [];
527
+ const seenKeys = new Set();
528
+ for (const prop of rhs.properties) {
529
+ if (prop.type !== 'Property') return null;
530
+ if (prop.kind !== 'init') return null;
531
+ if (prop.computed || prop.method) return null;
532
+ if (prop.key.type !== 'Identifier') return null;
533
+ const key = prop.key.name;
534
+ if (key === '__proto__' || key === 'prototype') return null;
535
+ if (!isValidExportName(key)) return null;
536
+ if (seenKeys.has(key)) return null;
537
+ const value = prop.value.type === 'Identifier' && prop.shorthand ? prop.key : prop.value;
538
+ if (!isAllowedRhs(value)) return null;
539
+ if (expressionHasRequireCall(value, requireShadowed)) return null;
540
+ const rhsSrc = rhsSourceFor(value);
541
+ if (value.type === 'Identifier' && value.name === key) {
542
+ exportsOut.push(`export { ${key} };`);
543
+ } else if (value.type === 'Identifier') {
544
+ exportsOut.push(`export { ${rhsSrc} as ${key} };`);
545
+ } else {
546
+ exportsOut.push(`export const ${key} = ${rhsSrc};`);
547
+ }
548
+ seenKeys.add(key);
549
+ }
550
+ exportsOut.push(`export default ${rhsSourceFor(rhs)};`);
551
+ return {
552
+ exportsOut,
553
+ seenKeys
554
+ };
555
+ };
523
556
  for (const entry of entries) {
524
557
  const write = entry.writes[0];
525
558
  if (write.type !== 'AssignmentExpression') {
@@ -538,7 +571,7 @@ const format = async (src, ast, opts) => {
538
571
  const base = left.object;
539
572
  const propName = left.property.name;
540
573
  const baseIsExports = base.type === 'Identifier' && base.name === 'exports';
541
- const baseIsModuleExports = base.type === 'MemberExpression' && base.object.type === 'Identifier' && base.object.name === 'module' && base.property.type === 'Identifier' && base.property.name === 'exports';
574
+ const baseIsModuleExports = base.type === 'Identifier' && base.name === 'module' && propName === 'exports' || base.type === 'MemberExpression' && base.object.type === 'Identifier' && base.object.name === 'module' && base.property.type === 'Identifier' && base.property.name === 'exports';
542
575
  if (!baseIsExports && !baseIsModuleExports) {
543
576
  return {
544
577
  ok: false,
@@ -546,25 +579,35 @@ const format = async (src, ast, opts) => {
546
579
  };
547
580
  }
548
581
  const rhs = write.right;
549
- if (!isAllowedRhs(rhs)) return {
550
- ok: false,
551
- reason: 'unsupported-rhs'
552
- };
553
- if (expressionHasRequireCall(rhs, requireShadowed)) {
554
- return {
582
+ const objectLiteralPlan = tryObjectLiteralExport(rhs, baseIsModuleExports, propName);
583
+ if (!objectLiteralPlan) {
584
+ if (!isAllowedRhs(rhs)) return {
555
585
  ok: false,
556
- reason: 'rhs-require'
586
+ reason: 'unsupported-rhs'
557
587
  };
588
+ if (expressionHasRequireCall(rhs, requireShadowed)) {
589
+ return {
590
+ ok: false,
591
+ reason: 'rhs-require'
592
+ };
593
+ }
558
594
  }
559
595
  const rhsSrc = rhsSourceFor(rhs);
560
596
  if (propName === 'exports' && baseIsModuleExports) {
561
- // module.exports = ... handles default
562
- if (seen.has('default')) return {
563
- ok: false,
564
- reason: 'duplicate-default'
565
- };
566
- seen.add('default');
567
- exportsOut.push(`export default ${rhsSrc};`);
597
+ if (objectLiteralPlan) {
598
+ for (const line of objectLiteralPlan.exportsOut) {
599
+ exportsOut.push(line);
600
+ }
601
+ objectLiteralPlan.seenKeys.forEach(k => seen.add(k));
602
+ } else {
603
+ // module.exports = ... handles default
604
+ if (seen.has('default')) return {
605
+ ok: false,
606
+ reason: 'duplicate-default'
607
+ };
608
+ seen.add('default');
609
+ exportsOut.push(`export default ${rhsSrc};`);
610
+ }
568
611
  } else {
569
612
  if (seen.has(propName)) return {
570
613
  ok: false,
@@ -824,11 +867,14 @@ const format = async (src, ast, opts) => {
824
867
  code.overwrite(rep.start, rep.end, idiomaticPlan.exports[idx]);
825
868
  });
826
869
  } else {
827
- for (const rep of idiomaticPlan.replacements) {
828
- code.overwrite(rep.start, rep.end, ';');
829
- }
830
- if (idiomaticPlan.exports.length) {
831
- code.append(`\n${idiomaticPlan.exports.join('\n')}\n`);
870
+ const [first, ...rest] = idiomaticPlan.replacements;
871
+ if (first) {
872
+ code.overwrite(first.start, first.end, idiomaticPlan.exports.join('\n'));
873
+ }
874
+ for (const rep of rest) {
875
+ const original = code.slice(rep.start, rep.end);
876
+ const hasSemicolon = original.trimEnd().endsWith(';');
877
+ code.overwrite(rep.start, rep.end, hasSemicolon ? ';' : '');
832
878
  }
833
879
  }
834
880
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knighted/module",
3
- "version": "1.3.0-rc.0",
3
+ "version": "1.3.0",
4
4
  "description": "Bidirectional transform for ES modules and CommonJS.",
5
5
  "type": "module",
6
6
  "main": "dist/module.js",