Skip to content

Commit 1272df2

Browse files
respencer-nclclaude
andcommitted
Fix string literals that trigger ESM shim plugin rewriting
Strings containing `import '`, `import "`, or `import(` in the JS bundle cause esmShimPlugin/Vite to misinterpret them as ES module imports and corrupt the output. Split or rephrase all 7 occurrences across 4 shared-source files. Added comments explaining the constraint so future changes don't reintroduce the pattern. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 00e6259 commit 1272df2

4 files changed

Lines changed: 23 additions & 7 deletions

File tree

language/shared/src/main/scala/com/ossuminc/riddl/language/AST.scala

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -985,14 +985,24 @@ object AST:
985985
/** Check if this is a selective import (imports a specific definition) */
986986
def isSelective: Boolean = kindOpt.isDefined && selector.isDefined
987987

988+
// NOTE: The RIDDL keyword "import" must NOT appear adjacent to a
989+
// quote character (' or ") in any string literal that reaches the
990+
// JS bundle. ESM shim plugins (e.g. esmShimPlugin / Vite) scan
991+
// the compiled JS for patterns like import '… or import "…
992+
// and rewrite them, corrupting the output. We split the keyword
993+
// with string concatenation so the pattern never appears in the
994+
// bundle as a single token. Do NOT "simplify" these into a
995+
// single interpolated string.
996+
private val imp = "im" + "port"
997+
988998
def format: String =
989999
if isSelective then
9901000
val kindStr = kindOpt.getOrElse("")
9911001
val selectorStr = selector.map(_.value).getOrElse("")
9921002
val aliasStr = alias.map(a => s" as ${a.value}").getOrElse("")
993-
s"""import $kindStr $selectorStr from "${path.s}"$aliasStr"""
1003+
s"""$imp $kindStr $selectorStr from "${path.s}"$aliasStr"""
9941004
else
995-
s"""import "${path.s}""""
1005+
imp + " \"" + path.s + "\""
9961006
override def toString: String = format
9971007
end BASTImport
9981008

language/shared/src/main/scala/com/ossuminc/riddl/language/bast/BASTLoader.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,11 @@ object BASTLoader {
5858
}
5959
loaded += 1
6060
case Left(error) =>
61+
// NOTE: avoid "import '" in string literals — ESM shim
62+
// plugins misinterpret it as an ES module import statement.
6163
msgs += Messages.Message(
6264
bi.loc,
63-
s"Failed to load BAST import '${bi.path.s}': $error",
65+
s"Failed to load BAST file '${bi.path.s}': $error",
6466
Messages.Error
6567
)
6668
failed += 1

language/shared/src/main/scala/com/ossuminc/riddl/language/parsing/TopLevelParser.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,13 @@ object TopLevelParser:
8282
private def loadBASTImports(root: Root, baseURL: URL)(using pc: PlatformContext): (Root, Messages) = {
8383
if BASTLoader.hasUnloadedImports(root) then
8484
val result = BASTLoader.loadImports(root, baseURL)
85+
// NOTE: avoid "import(" in string literals — ESM shim plugins
86+
// misinterpret it as a dynamic ES module import() call.
8587
if result.failedCount > 0 then
86-
pc.log.warn(s"Failed to load ${result.failedCount} BAST import(s)")
88+
pc.log.warn(s"Failed to load ${result.failedCount} BAST file(s)")
8789
end if
8890
if result.loadedCount > 0 then
89-
pc.log.info(s"Loaded ${result.loadedCount} BAST import(s)")
91+
pc.log.info(s"Loaded ${result.loadedCount} BAST file(s)")
9092
end if
9193
(root, result.messages)
9294
else

passes/shared/src/main/scala/com/ossuminc/riddl/passes/validate/ValidationPass.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -496,9 +496,11 @@ case class ValidationPass(
496496
check(i.origin.nonEmpty, "Include has no source provided", Messages.Error, i.loc)
497497
}
498498

499+
// NOTE: avoid "import '" in string literals — ESM shim plugins
500+
// misinterpret it as an ES module import statement.
499501
private def validateBASTImport(bi: BASTImport, parents: Parents): Unit = {
500-
check(bi.path.s.nonEmpty, "BAST import has no path specified", Messages.Error, bi.loc)
501-
check(bi.path.s.endsWith(".bast"), s"BAST import path '${bi.path.s}' should end with .bast", Messages.Warning, bi.loc)
502+
check(bi.path.s.nonEmpty, "BAST load has no path specified", Messages.Error, bi.loc)
503+
check(bi.path.s.endsWith(".bast"), s"BAST load path '${bi.path.s}' should end with .bast", Messages.Warning, bi.loc)
502504
}
503505

504506
private def validateEntity(

0 commit comments

Comments
 (0)