feat: add ImportDeclaration bindings and InjectImport mutation#83
Merged
Conversation
Adds top-level ECMAScript import support to the codegen pipeline so callers can prepend value- and type-only named imports to generated TypeScript. - bindings/declarations.go: new ImportDeclaration (DeclarationType) and ImportSpecifier nodes. Only the named-imports form is modeled; default and namespace imports can be added later. - typescript-engine/src/index.ts: factory wrappers importSpecifier and importDeclaration around ts.factory.createImportSpecifier and createImportDeclaration. dist/main.js is rebuilt to ship them. - bindings/bindings.go: dispatch *ImportDeclaration through ToTypescriptDeclarationNode and add the goja-backed builders. - convert.go: new imports field on *Typescript, AppendImport that merges by module specifier (unioning specifiers in first-seen order, IsTypeOnly only when all contributing imports are type-only), and emission in SerializeInOrder ahead of the named declarations. - config/imports.go: InjectImport and InjectTypeImport mutations. Names may use the "Name=Alias" form to emit "Name as Alias". - Tests: goja round-trip cases for ImportDeclaration, end-to-end ApplyMutations + Serialize coverage proving imports appear above the generated header's declarations and that repeated calls merge per module. Foundation for a follow-up that rewrites the zod string-builder serializer in PR #82 as a mutation that composes with ExportTypes, SimplifyOptional, and the other config mutations. Generated by Coder Agents on behalf of @Emyrk.
Member
Author
|
/coder-agent-review |
Review pass on the import primitives:
- AppendImport now keys by (Module, IsTypeOnly, SideEffect) instead of
Module alone. Mixing a type-only and a value import for the same module
used to flatten the declaration-level IsTypeOnly flag with `&&`, silently
demoting the type-only specifier into a value import. They now emit as
two distinct statements.
- Serialize sorts the import block by (Module, !SideEffect, IsTypeOnly)
and each statement's specifiers by (Name, Alias, IsTypeOnly). Output is
deterministic across mutation orderings.
- New `SideEffect bool` on bindings.ImportDeclaration plus
config.InjectSideEffectImport(module) emit the bare `import "module";`
form. The TS factory wrapper drops the import clause entirely when no
specifiers are supplied so the printer outputs the side-effect form
natively rather than `import { } from "module"`.
- bindings/imports_test.go covers the bare and bare-plus-stray-IsTypeOnly
shapes through the goja round trip. imports_test.go replaces the single
smoke test with a per-behavior suite: merge dedup, alias + type form,
mixed-mode split, alphabetical module order, side-effect dedup and
placement before clausal imports for the same module.
Generated by Coder Agents on behalf of @Emyrk.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds top-level
import { ... } from "...",import type { ... }, and bareimport "..."support so callers can prepend imports to the generated TypeScript output.Foundation for a follow-up that rewrites #82 as a guts mutation composable with
ExportTypes,SimplifyOptional, etc.Design notes, merge semantics, sort order, test list
File-by-file
bindings/declarations.go: newImportDeclaration(DeclarationType) andImportSpecifier. Modeled forms: named, type-only, side-effect. Default and namespace imports are not yet supported; doc comments call this out.typescript-engine/src/index.ts:importSpecifierandimportDeclarationwrappers aroundts.factory.createImportSpecifier/createImportDeclaration/createImportClause/createNamedImports. When the named-imports array isundefinedthe wrapper drops the import clause so the printer emits the side-effect form natively.dist/main.jsis rebuilt withpnpm run build.bindings/bindings.go:*ImportDeclarationdispatched throughToTypescriptDeclarationNodeplus the goja-backed builders. EmptyAliasemits just the name; a setAliaspasses the original name as the TSpropertyNameso the printer emitsName as Alias.convert.go:imports []*bindings.ImportDeclarationfield onTypescript, publicAppendImport(decl)that merges by(Module, IsTypeOnly, SideEffect), and an emission block inSerializeInOrderthat writes the imports above the alphabetically-sorted declarations.config/imports.go: the user-facing mutation API.Merge semantics for
AppendImportImports are keyed by
(Module, IsTypeOnly, SideEffect):(Name, Alias, IsTypeOnly); duplicates dropped while preserving first-occurrence order.InjectTypeImport("zod", "Foo")+InjectImport("zod", "bar")emits two lines, notimport { Foo, bar } from "zod";withFoodemoted.Deterministic emission order
Serializesorts before emitting so output is independent of mutation order:(Module asc, !SideEffect, IsTypeOnly asc). Same module groups together with side-effect first, then value, then type-only.(Name, Alias, IsTypeOnly).Mutation API details
"Name=Alias"shorthand inInjectImport/InjectTypeImportemitsName as Alias; bare names emit as-is.InjectSideEffectImport(module)takes no names.Tests
bindings/imports_test.go: goja round-trip for single, multiple, aliased, type-only, mixed, side-effect, side-effect-with-stray-IsTypeOnly.config/imports_internal_test.go:Name=Aliasparser.imports_test.go(root), per-behavior subtests:TestInjectImport_Merge— same-module dedup, specifiers sorted.TestInjectImport_AliasAndType— alias shorthand + type form.TestInjectImport_MixedTypeAndValueSplit— regression for the mixed-mode flatten bug.TestInjectImport_SortedAcrossModules— alphabetical regardless of append order.TestInjectSideEffectImport— bare emit, dedupe, side-effect-before-clausal placement.go test ./...andgo vet ./...pass (one pre-existing unreachable-code warning inconvert.gopredates this branch).Follow-up
Stacked PR will replace the #82 string-builder serializer with a
zod.AsSchemasmutation emittingVariableStatement+Aliaspairs. That needsImportDeclaration(this PR) plus new expression nodes forCallExpression,PropertyAccessExpression,ObjectLiteralExpression,ArrayLiteralExpression,ArrowFunction, andTypeQuery.Generated by Coder Agents on behalf of @Emyrk.