GEN2: the seat generator emits each TypeScript contract as index.js with its index.d.ts
A seat's npm package holds index.js, index.d.ts and parts/<part>.js with .d.ts, with no TypeScript source and no tsconfig.json, so a TypeScript module imports it with no build step and no tsc of the contract. Builds: design 47 G §341 Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
@@ -5,6 +5,8 @@ import (
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
re "regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -79,7 +81,7 @@ func TestEachSeatDefinedWholeIsAGoModuleOfItsOwn(t *testing.T) {
|
||||
!(strings.Contains(src, "\t\"time\"\n") || strings.Contains(src, "import \"time\"")) {
|
||||
t.Errorf("%s does not write a date-time as time.Time:\n%s", seat, src)
|
||||
}
|
||||
if ts := string(files["ts/seats/"+seat+"/src/parts/registry.ts"]); !strings.Contains(ts, `"pushed": string;`) || !strings.Contains(ts, `"at"?: string;`) {
|
||||
if ts := string(files["ts/seats/"+seat+"/parts/registry.d.ts"]); !strings.Contains(ts, `"pushed": string;`) || !strings.Contains(ts, `"at"?: string;`) {
|
||||
t.Errorf("%s's TypeScript does not write a date-time as a string:\n%s", seat, ts)
|
||||
}
|
||||
// A scalar typed ["T","null"] is a pointer in Go and `T | null` in TypeScript, so a declared zero survives with its type.
|
||||
@@ -88,7 +90,7 @@ func TestEachSeatDefinedWholeIsAGoModuleOfItsOwn(t *testing.T) {
|
||||
t.Errorf("%s does not write a nullable scalar as a pointer (%s):\n%s", seat, w, src)
|
||||
}
|
||||
}
|
||||
if ts := string(files["ts/seats/"+seat+"/src/parts/registry.ts"]); !strings.Contains(ts, `"floor"?: number | null;`) || !strings.Contains(ts, `"on"?: boolean | null;`) || !strings.Contains(ts, `"label"?: string | null;`) {
|
||||
if ts := string(files["ts/seats/"+seat+"/parts/registry.d.ts"]); !strings.Contains(ts, `"floor"?: number | null;`) || !strings.Contains(ts, `"on"?: boolean | null;`) || !strings.Contains(ts, `"label"?: string | null;`) {
|
||||
t.Errorf("%s's TypeScript does not write a nullable scalar as T | null:\n%s", seat, ts)
|
||||
}
|
||||
// A string's enum is a constant per value, so a caller names the definition's value (A3t: the removal reason).
|
||||
@@ -139,8 +141,8 @@ func TestEachSeatDefinedWholeIsAGoModuleOfItsOwn(t *testing.T) {
|
||||
dir = d
|
||||
}
|
||||
}
|
||||
if dir == "" || !strings.HasPrefix(p, dir+"src/") {
|
||||
t.Errorf("%s is in no npm package's src", p)
|
||||
if dir == "" || (p != dir+"index.d.ts" && !strings.HasPrefix(p, dir+"parts/")) {
|
||||
t.Errorf("%s is in no npm package's files", p)
|
||||
}
|
||||
for _, line := range strings.Split(string(b), "\n") {
|
||||
i := strings.Index(line, ` from "`)
|
||||
@@ -172,8 +174,8 @@ func TestEachSeatDefinedWholeIsAGoModuleOfItsOwn(t *testing.T) {
|
||||
}
|
||||
}
|
||||
for _, seat := range []string{"aregistry", "bregistry"} {
|
||||
if !strings.Contains(string(files["ts/seats/"+seat+"/src/index.ts"]), "export type PublishedEvent = registry.PublishedEvent") ||
|
||||
!strings.Contains(string(files["ts/seats/"+seat+"/src/parts/registry.ts"]), "export type PublishedEvent") {
|
||||
if !strings.Contains(string(files["ts/seats/"+seat+"/index.d.ts"]), "export type PublishedEvent = registry.PublishedEvent") ||
|
||||
!strings.Contains(string(files["ts/seats/"+seat+"/parts/registry.d.ts"]), "export type PublishedEvent") {
|
||||
t.Errorf("the npm package of %s does not hold the part's types whole", seat)
|
||||
}
|
||||
}
|
||||
@@ -232,3 +234,60 @@ func TestCallersAreTheFormsOfTheFormat(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestATypeScriptContractIsJavaScriptWithItsDeclarations is design 47 G §341's check: each seat's npm package holds the
|
||||
// contract as index.js with its index.d.ts (and each part the same), no TypeScript source and no tsconfig, so a
|
||||
// TypeScript module imports it with no build step and no tsc of the contract.
|
||||
func TestATypeScriptContractIsJavaScriptWithItsDeclarations(t *testing.T) {
|
||||
m, err := Load("testdata/contracts", "example.org/contracts/go", "@example/contracts")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
files, err := Generate(m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, want := range []string{"index.js", "index.d.ts", "parts/registry.js", "parts/registry.d.ts", "package.json", "package-lock.json"} {
|
||||
if _, ok := files["ts/seats/aregistry/"+want]; !ok {
|
||||
t.Errorf("the seat's npm package lacks %s", want)
|
||||
}
|
||||
}
|
||||
for p, b := range files {
|
||||
if !strings.HasPrefix(p, "ts/") {
|
||||
continue
|
||||
}
|
||||
if (strings.HasSuffix(p, ".ts") && !strings.HasSuffix(p, ".d.ts")) || strings.HasSuffix(p, "tsconfig.json") {
|
||||
t.Errorf("%s is TypeScript source or a compiler's setting: the contract needs no tsc", p)
|
||||
}
|
||||
if strings.HasSuffix(p, ".js") {
|
||||
for _, typed := range []string{": string", "interface ", " as const", "export type ", "import type", "declare "} {
|
||||
if strings.Contains(string(b), typed) {
|
||||
t.Errorf("%s holds TypeScript syntax %q: it is JavaScript", p, typed)
|
||||
}
|
||||
}
|
||||
if node, err := exec.LookPath("node"); err == nil {
|
||||
path := filepath.Join(t.TempDir(), "check.mjs")
|
||||
if err := os.WriteFile(path, b, 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if out, err := exec.Command(node, "--check", path).CombinedOutput(); err != nil {
|
||||
t.Errorf("%s is no JavaScript module: %v\n%s", p, err, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var pkg struct {
|
||||
Types string `json:"types"`
|
||||
Exports map[string]map[string]string `json:"exports"`
|
||||
Scripts map[string]string `json:"scripts"`
|
||||
}
|
||||
if err := json.Unmarshal(files["ts/seats/aregistry/package.json"], &pkg); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if pkg.Types != "./index.d.ts" || pkg.Exports["."]["default"] != "./index.js" || pkg.Exports["."]["types"] != "./index.d.ts" || len(pkg.Scripts) != 0 {
|
||||
t.Errorf("package.json says %+v", pkg)
|
||||
}
|
||||
if !strings.Contains(string(files["ts/seats/aregistry/index.d.ts"]), `import type * as registry from "./parts/registry.js";`) {
|
||||
t.Error("the declarations do not import the part")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,14 +203,14 @@ func goStatesImports(states []seatdef.State) string {
|
||||
return out + ")\n\n"
|
||||
}
|
||||
|
||||
// tsStates writes a seat's states for TypeScript: the local names, and for each key form its builder and parser (the
|
||||
// value's type is declared with the seat's other types).
|
||||
func tsStates(b *bytes.Buffer, states []seatdef.State) error {
|
||||
// tsStates writes a seat's states for TypeScript: the local names, and for each key form its builder and parser, as
|
||||
// JavaScript and as declarations (the value's type is declared with the seat's other types).
|
||||
func tsStates(o *tsOut, states []seatdef.State) error {
|
||||
names := make([]string, len(states))
|
||||
for i, s := range states {
|
||||
names[i] = s.Name
|
||||
}
|
||||
tsNames(b, "states", names)
|
||||
tsNames(o, "states", names)
|
||||
for _, s := range states {
|
||||
for _, kf := range s.KeyForms {
|
||||
ts, err := keyTokens(kf.Key)
|
||||
@@ -218,11 +218,12 @@ func tsStates(b *bytes.Buffer, states []seatdef.State) error {
|
||||
return fmt.Errorf("state %s: %w", s.Name, err)
|
||||
}
|
||||
fn := tsName(formIdent(s.Name, kf.Key, ts))
|
||||
var args, expr, fields, got []string
|
||||
var args, params, expr, fields, got []string
|
||||
for _, t := range ts {
|
||||
if t.part {
|
||||
p := paramName(t.text)
|
||||
args = append(args, p+": string")
|
||||
params = append(params, p)
|
||||
expr = append(expr, p)
|
||||
fields = append(fields, p+": string")
|
||||
got = append(got, p)
|
||||
@@ -230,10 +231,17 @@ func tsStates(b *bytes.Buffer, states []seatdef.State) error {
|
||||
expr = append(expr, strconv.Quote(t.text))
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(b, "/** The key of one value of the state %s under the form %s. */\nexport function %sKey(%s): string {\n return %s;\n}\n\n",
|
||||
s.Name, strconv.Quote(kf.Key), fn, strings.Join(args, ", "), strings.Join(expr, " + "))
|
||||
fmt.Fprintf(b, "/** The parts of a key of the form %s, or undefined for a key of another form. */\nexport function parse%sKey(_key: string): { %s } | undefined {\n let _rest = _key;\n",
|
||||
strconv.Quote(kf.Key), ident(fn), strings.Join(fields, "; "))
|
||||
doc := fmt.Sprintf("/** The key of one value of the state %s under the form %s. */\n", s.Name, strconv.Quote(kf.Key))
|
||||
o.dts.WriteString(doc)
|
||||
fmt.Fprintf(&o.dts, "export declare function %sKey(%s): string;\n\n", fn, strings.Join(args, ", "))
|
||||
o.js.WriteString(doc)
|
||||
fmt.Fprintf(&o.js, "export function %sKey(%s) {\n return %s;\n}\n\n", fn, strings.Join(params, ", "), strings.Join(expr, " + "))
|
||||
doc = fmt.Sprintf("/** The parts of a key of the form %s, or undefined for a key of another form. */\n", strconv.Quote(kf.Key))
|
||||
o.dts.WriteString(doc)
|
||||
fmt.Fprintf(&o.dts, "export declare function parse%sKey(_key: string): { %s } | undefined;\n\n", ident(fn), strings.Join(fields, "; "))
|
||||
b := &o.js
|
||||
b.WriteString(doc)
|
||||
fmt.Fprintf(b, "export function parse%sKey(_key) {\n let _rest = _key;\n", ident(fn))
|
||||
for i := 0; i < len(ts); {
|
||||
t := ts[i]
|
||||
switch {
|
||||
|
||||
@@ -41,14 +41,20 @@ func TestAStateHasItsNamesKeyFormsAndStrictValues(t *testing.T) {
|
||||
if strings.Contains(src, "Record") {
|
||||
t.Errorf("the seat's package still names a record:\n%s", src)
|
||||
}
|
||||
ts := string(files["ts/seats/aregistry/src/index.ts"])
|
||||
for _, want := range []string{"export const states = {", "export function asksByAskerIDKey(asker: string, id: string): string",
|
||||
"export function parseAsksByAskerIDKey(_key: string)", "export interface AsksByAskerIDValue",
|
||||
// a key form with a part named key: the parser's own parameter and locals are named apart from the parts
|
||||
"export function parseAsksByNodeModuleKeyKey(_key: string): { node: string; module: string; key: string } | undefined",
|
||||
"const key = _rest;"} {
|
||||
if !strings.Contains(ts, want) {
|
||||
t.Errorf("the seat's npm package lacks %q:\n%s", want, ts)
|
||||
js := string(files["ts/seats/aregistry/index.js"])
|
||||
dts := string(files["ts/seats/aregistry/index.d.ts"])
|
||||
for file, wants := range map[string][]string{
|
||||
js: {"export const states = {", "export function asksByAskerIDKey(asker, id) {", "export function parseAsksByAskerIDKey(_key) {",
|
||||
"export function parseAsksByNodeModuleKeyKey(_key) {", "const key = _rest;"},
|
||||
dts: {"export declare const states: {", "export declare function asksByAskerIDKey(asker: string, id: string): string;",
|
||||
"export declare function parseAsksByAskerIDKey(_key: string)", "export interface AsksByAskerIDValue",
|
||||
// a key form with a part named key: the parser's own parameter and locals are named apart from the parts
|
||||
"export declare function parseAsksByNodeModuleKeyKey(_key: string): { node: string; module: string; key: string } | undefined;"},
|
||||
} {
|
||||
for _, want := range wants {
|
||||
if !strings.Contains(file, want) {
|
||||
t.Errorf("the seat's npm package lacks %q:\n%s", want, file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,31 +19,17 @@ func TSSeatDir(seat string) string { return "ts/seats/" + pkgName(seat) }
|
||||
// TSSeatPackage is the npm package name of a seat defined whole, beside the seat set's package root.
|
||||
func TSSeatPackage(root, seat string) string { return root + "-" + seat }
|
||||
|
||||
// tsConfig type-checks the package's sources and writes nothing: the package ships its TypeScript, which its importers
|
||||
// compile or bundle, so it has no build script and needs no compiler of its own (F7: a generated package has no
|
||||
// dependency, and its lock, which the build module requires of a package artifact, holds none).
|
||||
const tsConfig = `{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"noEmit": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
},
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
`
|
||||
|
||||
// tsPackage writes one npm package's package.json, its dependency-free package-lock.json and its tsconfig.json at dir.
|
||||
// tsPackage writes one npm package's package.json and its dependency-free package-lock.json at dir. The package ships
|
||||
// its contract as JavaScript with its declarations (design 47 G §341): its importers need no build step of its own and
|
||||
// nothing runs tsc over it, so it has no tsconfig.json and no script (F7: a generated package has no dependency, and its
|
||||
// lock, which the build module requires of a package artifact, holds none).
|
||||
func tsPackage(out map[string][]byte, dir, name, description string) {
|
||||
pkg := map[string]any{
|
||||
"name": name, "version": "0.1.0", "type": "module",
|
||||
"description": description,
|
||||
"exports": map[string]any{".": "./src/index.ts"},
|
||||
"types": "./src/index.ts",
|
||||
"files": []any{"src"},
|
||||
"exports": map[string]any{".": map[string]any{"types": "./index.d.ts", "default": "./index.js"}},
|
||||
"types": "./index.d.ts",
|
||||
"files": []any{"index.js", "index.d.ts", "parts"},
|
||||
}
|
||||
raw, _ := json.MarshalIndent(pkg, "", " ")
|
||||
out[dir+"/package.json"] = append(raw, '\n')
|
||||
@@ -53,13 +39,18 @@ func tsPackage(out map[string][]byte, dir, name, description string) {
|
||||
}
|
||||
raw, _ = json.MarshalIndent(lock, "", " ")
|
||||
out[dir+"/package-lock.json"] = append(raw, '\n')
|
||||
out[dir+"/tsconfig.json"] = []byte(tsConfig)
|
||||
}
|
||||
|
||||
// tsFiles writes the npm side: one package per seat defined whole at TSSeatDir (ADR 0625 §1), holding the parts it includes whole and importing nothing outside it.
|
||||
// tsOut is one contract file as its two forms: the JavaScript module a TypeScript module imports and the declarations
|
||||
// that type it (design 47 G §341).
|
||||
type tsOut struct{ js, dts bytes.Buffer }
|
||||
|
||||
// tsFiles writes the npm side: one package per seat defined whole at TSSeatDir (ADR 0625 §1), holding the parts it
|
||||
// includes whole and importing nothing outside it. Each file is a JavaScript module with its declarations beside it
|
||||
// (`index.js` and `index.d.ts`, `parts/<part>.js` and `parts/<part>.d.ts`; design 47 G §341).
|
||||
func tsFiles(m *Module, out map[string][]byte) error {
|
||||
partTypes := map[string]*typer{}
|
||||
partFiles := map[string][]byte{}
|
||||
partFiles := map[string]*tsOut{}
|
||||
for _, name := range sortedKeys(m.Parts) {
|
||||
p := m.Parts[name]
|
||||
d := seatdef.Definition{Grain: seatdef.Whole, Verbs: p.Verbs, Events: p.Events, Defs: p.Defs}
|
||||
@@ -68,13 +59,17 @@ func tsFiles(m *Module, out map[string][]byte) error {
|
||||
return err
|
||||
}
|
||||
partTypes[name] = tp
|
||||
var b bytes.Buffer
|
||||
b.WriteString(tsHeader)
|
||||
fmt.Fprintf(&b, "// The part %q several seats include whole: %s\n\n", p.Part, oneLine(p.Description))
|
||||
tsNames(&b, "verbs", seatdef.VerbNames(p.Verbs))
|
||||
tsNames(&b, "events", seatdef.MessageNames(p.Events))
|
||||
tsTypes(&b, tp)
|
||||
partFiles[name] = b.Bytes()
|
||||
o := &tsOut{}
|
||||
o.js.WriteString(tsHeader)
|
||||
o.dts.WriteString(tsHeader)
|
||||
note := fmt.Sprintf("// The part %q several seats include whole: %s\n\n", p.Part, oneLine(p.Description))
|
||||
o.js.WriteString(note)
|
||||
o.dts.WriteString(note)
|
||||
tsNames(o, "verbs", seatdef.VerbNames(p.Verbs))
|
||||
tsNames(o, "events", seatdef.MessageNames(p.Events))
|
||||
o.js.WriteString("export {};\n")
|
||||
tsTypes(&o.dts, tp)
|
||||
partFiles[name] = o
|
||||
}
|
||||
for _, name := range m.Order {
|
||||
d := m.Seats[name]
|
||||
@@ -89,67 +84,74 @@ func tsFiles(m *Module, out map[string][]byte) error {
|
||||
for _, t := range partTypes[inc].types {
|
||||
fromPart[t.name] = inc
|
||||
}
|
||||
out[dir+"/src/parts/"+inc+".ts"] = partFiles[inc]
|
||||
out[dir+"/parts/"+inc+".js"] = partFiles[inc].js.Bytes()
|
||||
out[dir+"/parts/"+inc+".d.ts"] = partFiles[inc].dts.Bytes()
|
||||
}
|
||||
tp, err := declareAll(d, fromPart)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var b bytes.Buffer
|
||||
b.WriteString(tsHeader)
|
||||
fmt.Fprintf(&b, "// The contract of the seat %q, defined whole (seats/%s/seat.json): %s\n\n", d.Seat, d.Seat, oneLine(d.Description))
|
||||
o := &tsOut{}
|
||||
o.js.WriteString(tsHeader)
|
||||
o.dts.WriteString(tsHeader)
|
||||
note := fmt.Sprintf("// The contract of the seat %q, defined whole (seats/%s/seat.json): %s\n\n", d.Seat, d.Seat, oneLine(d.Description))
|
||||
o.js.WriteString(note)
|
||||
o.dts.WriteString(note)
|
||||
for _, inc := range raw.Includes {
|
||||
fmt.Fprintf(&b, "import type * as %s from \"./parts/%s.js\";\n\n", tsName(inc), inc)
|
||||
fmt.Fprintf(&o.dts, "import type * as %s from \"./parts/%s.js\";\n\n", tsName(inc), inc)
|
||||
}
|
||||
fmt.Fprintf(&b, "export const seat = %q;\n\n", d.Seat)
|
||||
tsNames(&b, "verbs", seatdef.VerbNames(d.Verbs))
|
||||
tsNames(&b, "accepts", seatdef.MessageNames(d.Accepts))
|
||||
tsNames(&b, "events", seatdef.MessageNames(d.Events))
|
||||
fmt.Fprintf(&o.js, "export const seat = %q;\n\n", d.Seat)
|
||||
fmt.Fprintf(&o.dts, "export declare const seat: %q;\n\n", d.Seat)
|
||||
tsNames(o, "verbs", seatdef.VerbNames(d.Verbs))
|
||||
tsNames(o, "accepts", seatdef.MessageNames(d.Accepts))
|
||||
tsNames(o, "events", seatdef.MessageNames(d.Events))
|
||||
if f := d.Family; f != nil {
|
||||
fmt.Fprintf(&b, "export const family = { name: %q, maxAgeDays: %d", f.Name, f.MaxAgeDays)
|
||||
b.WriteString(" } as const;\n\n")
|
||||
fmt.Fprintf(&o.js, "export const family = { name: %q, maxAgeDays: %d };\n\n", f.Name, f.MaxAgeDays)
|
||||
fmt.Fprintf(&o.dts, "export declare const family: { readonly name: %q; readonly maxAgeDays: %d };\n\n", f.Name, f.MaxAgeDays)
|
||||
}
|
||||
for _, e := range d.Events {
|
||||
subj := seatdef.EventSubject(d.Seat, e.Name)
|
||||
if e.ByCaller {
|
||||
fmt.Fprintf(&b, "export function %sSubject(caller: string): string {\n return %q + caller;\n}\n\n", tsName(e.Name), subj+".")
|
||||
} else {
|
||||
fmt.Fprintf(&b, "export const %sSubject = %q;\n\n", tsName(e.Name), subj)
|
||||
}
|
||||
tsSubject(o, tsName(e.Name), seatdef.EventSubject(d.Seat, e.Name), e.ByCaller)
|
||||
}
|
||||
for _, a := range d.Accepts {
|
||||
subj := seatdef.AcceptSubject(d.Seat, a.Name)
|
||||
if a.ByCaller {
|
||||
fmt.Fprintf(&b, "export function %sSubject(caller: string): string {\n return %q + caller;\n}\n\n", tsName(a.Name), subj+".")
|
||||
} else {
|
||||
fmt.Fprintf(&b, "export const %sSubject = %q;\n\n", tsName(a.Name), subj)
|
||||
}
|
||||
tsSubject(o, tsName(a.Name), seatdef.AcceptSubject(d.Seat, a.Name), a.ByCaller)
|
||||
}
|
||||
if len(d.HolderEvents) > 0 {
|
||||
tsNames(&b, "holderEvents", holderNames(d.HolderEvents))
|
||||
tsNames(o, "holderEvents", holderNames(d.HolderEvents))
|
||||
for _, e := range d.HolderEvents {
|
||||
before, after, _ := strings.Cut(e.Subject, "<holder>")
|
||||
fmt.Fprintf(&b, "export function %sHolderSubject(holder: string): string {\n return %q + holder + %q;\n}\n\n",
|
||||
tsName(e.Name), before, after)
|
||||
fmt.Fprintf(&o.js, "export function %sHolderSubject(holder) {\n return %q + holder + %q;\n}\n\n", tsName(e.Name), before, after)
|
||||
fmt.Fprintf(&o.dts, "export declare function %sHolderSubject(holder: string): string;\n\n", tsName(e.Name))
|
||||
}
|
||||
}
|
||||
if len(d.States) > 0 {
|
||||
if err := tsStates(&b, d.States); err != nil {
|
||||
if err := tsStates(o, d.States); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, n := range sortedKeys(fromPart) {
|
||||
fmt.Fprintf(&b, "export type %s = %s.%s;\n", n, tsName(fromPart[n]), n)
|
||||
fmt.Fprintf(&o.dts, "export type %s = %s.%s;\n", n, tsName(fromPart[n]), n)
|
||||
}
|
||||
if len(fromPart) > 0 {
|
||||
b.WriteString("\n")
|
||||
o.dts.WriteString("\n")
|
||||
}
|
||||
tsTypes(&b, tp)
|
||||
out[dir+"/src/index.ts"] = b.Bytes()
|
||||
tsTypes(&o.dts, tp)
|
||||
out[dir+"/index.js"] = o.js.Bytes()
|
||||
out[dir+"/index.d.ts"] = o.dts.Bytes()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// tsSubject writes the constant, or for a subject ending in the caller's name the function, of one subject.
|
||||
func tsSubject(o *tsOut, name, subj string, byCaller bool) {
|
||||
if byCaller {
|
||||
fmt.Fprintf(&o.js, "export function %sSubject(caller) {\n return %q + caller;\n}\n\n", name, subj+".")
|
||||
fmt.Fprintf(&o.dts, "export declare function %sSubject(caller: string): string;\n\n", name)
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(&o.js, "export const %sSubject = %q;\n\n", name, subj)
|
||||
fmt.Fprintf(&o.dts, "export declare const %sSubject: %q;\n\n", name, subj)
|
||||
}
|
||||
|
||||
func holderNames(es []seatdef.HolderEvent) []string {
|
||||
out := make([]string, len(es))
|
||||
for i, e := range es {
|
||||
@@ -158,15 +160,18 @@ func holderNames(es []seatdef.HolderEvent) []string {
|
||||
return out
|
||||
}
|
||||
|
||||
func tsNames(b *bytes.Buffer, what string, names []string) {
|
||||
func tsNames(o *tsOut, what string, names []string) {
|
||||
if len(names) == 0 {
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(b, "export const %s = {\n", what)
|
||||
fmt.Fprintf(&o.js, "export const %s = {\n", what)
|
||||
fmt.Fprintf(&o.dts, "export declare const %s: {\n", what)
|
||||
for _, n := range names {
|
||||
fmt.Fprintf(b, " %s: %q,\n", tsName(n), n)
|
||||
fmt.Fprintf(&o.js, " %s: %q,\n", tsName(n), n)
|
||||
fmt.Fprintf(&o.dts, " readonly %s: %q;\n", tsName(n), n)
|
||||
}
|
||||
b.WriteString("} as const;\n\n")
|
||||
o.js.WriteString("};\n\n")
|
||||
o.dts.WriteString("};\n\n")
|
||||
}
|
||||
|
||||
func tsTypes(b *bytes.Buffer, tp *typer) {
|
||||
|
||||
Reference in New Issue
Block a user