@smoothbricks/lmao-ttsc 0.0.0-bootstrap.0 → 0.1.7
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.
- package/dist/bun-register.d.ts +2 -0
- package/dist/bun-register.d.ts.map +1 -0
- package/dist/bun-register.js +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/package.json +113 -4
- package/plugin/go.mod +9 -0
- package/plugin/loginline.go +296 -0
- package/plugin/main.go +572 -0
- package/plugin/main_test.go +43 -0
- package/plugin/optimization.go +462 -0
- package/plugin/resultinline.go +211 -0
- package/plugin/taginline.go +453 -0
- package/plugin/template_test.go +264 -0
- package/plugin.cjs +19 -0
- package/plugin.d.cts +5 -0
- package/README.md +0 -3
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"os"
|
|
5
|
+
"path/filepath"
|
|
6
|
+
"strings"
|
|
7
|
+
"testing"
|
|
8
|
+
|
|
9
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
10
|
+
shimprinter "github.com/microsoft/typescript-go/shim/printer"
|
|
11
|
+
"github.com/samchon/ttsc/packages/ttsc/driver"
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
const templateFixtureDeclarations = `
|
|
15
|
+
export interface OpCompileMetadata {
|
|
16
|
+
readonly runtimeHint: number;
|
|
17
|
+
readonly logTemplateIds: readonly string[];
|
|
18
|
+
}
|
|
19
|
+
export class FluentLogEntry { line(value: number): this; }
|
|
20
|
+
export class SpanLogger {
|
|
21
|
+
info(message: string): FluentLogEntry;
|
|
22
|
+
debug(message: string): FluentLogEntry;
|
|
23
|
+
warn(message: string): FluentLogEntry;
|
|
24
|
+
error(message: string): FluentLogEntry;
|
|
25
|
+
trace(message: string): FluentLogEntry;
|
|
26
|
+
}
|
|
27
|
+
export class SpanContext {
|
|
28
|
+
readonly _buffer: { constructor: unknown; _opMetadata: unknown };
|
|
29
|
+
readonly log: SpanLogger;
|
|
30
|
+
ok(value: unknown): unknown;
|
|
31
|
+
span(name: string, op: Op): Promise<unknown>;
|
|
32
|
+
}
|
|
33
|
+
export class Op {
|
|
34
|
+
readonly SpanBufferClass: unknown;
|
|
35
|
+
readonly remappedViewClass: unknown;
|
|
36
|
+
readonly metadata: unknown;
|
|
37
|
+
readonly runtimeHint: number;
|
|
38
|
+
readonly fn: (ctx: SpanContext) => unknown;
|
|
39
|
+
}
|
|
40
|
+
export class OpGroup {}
|
|
41
|
+
export function defineOp(name: string, fn: (ctx: SpanContext) => unknown, metadata?: unknown, compileMetadata?: OpCompileMetadata): Op;
|
|
42
|
+
export function defineOps(definitions: Record<string, Op | ((ctx: SpanContext) => unknown)>, compileMetadataByKey?: Readonly<Record<string, OpCompileMetadata>>): OpGroup;
|
|
43
|
+
`
|
|
44
|
+
|
|
45
|
+
func transformTemplateFixture(t *testing.T, body string) string {
|
|
46
|
+
t.Helper()
|
|
47
|
+
root := t.TempDir()
|
|
48
|
+
inputPath := filepath.Join(root, "input.ts")
|
|
49
|
+
declarationDir := filepath.Join(root, "node_modules", "@smoothbricks", "lmao")
|
|
50
|
+
if err := os.MkdirAll(declarationDir, 0o755); err != nil {
|
|
51
|
+
t.Fatal(err)
|
|
52
|
+
}
|
|
53
|
+
source := "import { defineOp, defineOps, Op, SpanContext } from '@smoothbricks/lmao';\n" + body
|
|
54
|
+
files := map[string]string{
|
|
55
|
+
inputPath: source,
|
|
56
|
+
filepath.Join(root, "tsconfig.json"): `{"compilerOptions":{"target":"esnext","module":"esnext","moduleResolution":"node"},"files":["input.ts"]}`,
|
|
57
|
+
filepath.Join(declarationDir, "index.d.ts"): templateFixtureDeclarations,
|
|
58
|
+
filepath.Join(declarationDir, "package.json"): `{"name":"@smoothbricks/lmao","types":"index.d.ts"}`,
|
|
59
|
+
}
|
|
60
|
+
for name, content := range files {
|
|
61
|
+
if err := os.WriteFile(name, []byte(content), 0o644); err != nil {
|
|
62
|
+
t.Fatal(err)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
program, _, err := driver.LoadProgram(root, filepath.Join(root, "tsconfig.json"), driver.LoadProgramOptions{})
|
|
67
|
+
if err != nil {
|
|
68
|
+
t.Fatal(err)
|
|
69
|
+
}
|
|
70
|
+
defer program.Close()
|
|
71
|
+
transform := lmaoPluginTransform(program, root)
|
|
72
|
+
for _, sourceFile := range program.SourceFiles() {
|
|
73
|
+
if sourceFile == nil || filepath.Clean(sourceFile.FileName()) != filepath.Clean(inputPath) {
|
|
74
|
+
continue
|
|
75
|
+
}
|
|
76
|
+
emitContext := shimprinter.NewEmitContext()
|
|
77
|
+
result := transform(emitContext, sourceFile)
|
|
78
|
+
if result == nil {
|
|
79
|
+
t.Fatal("template fixture transform returned nil")
|
|
80
|
+
}
|
|
81
|
+
printer := shimprinter.NewPrinter(shimprinter.PrinterOptions{}, shimprinter.PrintHandlers{}, emitContext)
|
|
82
|
+
return shimprinter.EmitSourceFile(printer, result)
|
|
83
|
+
}
|
|
84
|
+
t.Fatal("template fixture input source was not loaded")
|
|
85
|
+
return ""
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
func collectNodeText(root *shimast.Node, kind shimast.Kind) []string {
|
|
89
|
+
var values []string
|
|
90
|
+
var visit func(*shimast.Node)
|
|
91
|
+
visit = func(node *shimast.Node) {
|
|
92
|
+
if node == nil {
|
|
93
|
+
return
|
|
94
|
+
}
|
|
95
|
+
if node.Kind == kind {
|
|
96
|
+
values = append(values, shimast.NodeText(node))
|
|
97
|
+
}
|
|
98
|
+
node.ForEachChild(func(child *shimast.Node) bool {
|
|
99
|
+
visit(child)
|
|
100
|
+
return false
|
|
101
|
+
})
|
|
102
|
+
}
|
|
103
|
+
visit(root)
|
|
104
|
+
return values
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
func containsText(values []string, target string) bool {
|
|
108
|
+
for _, value := range values {
|
|
109
|
+
if value == target {
|
|
110
|
+
return true
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return false
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
func emittedLogBlock(level string, templateID uint16) *shimast.Node {
|
|
117
|
+
list := factory.NewNodeList([]*shimast.Node{factory.NewExpressionStatement(ident("placeholder"))})
|
|
118
|
+
transformer := &fileTransformer{}
|
|
119
|
+
transformer.applyLogInlines([]logInline{{
|
|
120
|
+
list: list,
|
|
121
|
+
index: 0,
|
|
122
|
+
logExpr: propAccess(ident("ctx"), "log"),
|
|
123
|
+
level: level,
|
|
124
|
+
message: callExpr(ident("dynamicMessage"), nil),
|
|
125
|
+
templateID: templateID,
|
|
126
|
+
}})
|
|
127
|
+
return list.Nodes[0]
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
func TestCompileMetadataKeepsTemplateTableOrder(t *testing.T) {
|
|
131
|
+
node := compileMetadataNode(opCompileAnalysis{
|
|
132
|
+
runtimeHint: 123,
|
|
133
|
+
logTemplateIds: []string{"first", "shared", "last"},
|
|
134
|
+
})
|
|
135
|
+
strings := collectNodeText(node, shimast.KindStringLiteral)
|
|
136
|
+
if len(strings) != 3 || strings[0] != "first" || strings[1] != "shared" || strings[2] != "last" {
|
|
137
|
+
t.Fatalf("compile metadata template order = %q, want [first shared last]", strings)
|
|
138
|
+
}
|
|
139
|
+
identifiers := collectNodeText(node, shimast.KindIdentifier)
|
|
140
|
+
if !containsText(identifiers, "runtimeHint") || !containsText(identifiers, "logTemplateIds") {
|
|
141
|
+
t.Fatalf("compile metadata fields = %q, want runtimeHint and logTemplateIds", identifiers)
|
|
142
|
+
}
|
|
143
|
+
numbers := collectNodeText(node, shimast.KindNumericLiteral)
|
|
144
|
+
if !containsText(numbers, "123") {
|
|
145
|
+
t.Fatalf("compile metadata numeric values = %q, want runtimeHint 123", numbers)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
func TestLiteralLogInlineUsesOnlyTemplateLaneForEveryLevel(t *testing.T) {
|
|
150
|
+
entryTypes := map[string]string{
|
|
151
|
+
"info": "8",
|
|
152
|
+
"debug": "7",
|
|
153
|
+
"warn": "9",
|
|
154
|
+
"error": "10",
|
|
155
|
+
"trace": "6",
|
|
156
|
+
}
|
|
157
|
+
for level, entryType := range entryTypes {
|
|
158
|
+
t.Run(level, func(t *testing.T) {
|
|
159
|
+
block := emittedLogBlock(level, 37)
|
|
160
|
+
identifiers := collectNodeText(block, shimast.KindIdentifier)
|
|
161
|
+
if !containsText(identifiers, "_messageTemplateIds") {
|
|
162
|
+
t.Fatalf("%s literal inline does not write _messageTemplateIds: %q", level, identifiers)
|
|
163
|
+
}
|
|
164
|
+
if containsText(identifiers, "message_values") || containsText(identifiers, "message_nulls") {
|
|
165
|
+
t.Fatalf("%s literal inline writes dynamic message storage: %q", level, identifiers)
|
|
166
|
+
}
|
|
167
|
+
numbers := collectNodeText(block, shimast.KindNumericLiteral)
|
|
168
|
+
if !containsText(numbers, "37") {
|
|
169
|
+
t.Fatalf("%s literal inline numeric values = %q, want template ID 37", level, numbers)
|
|
170
|
+
}
|
|
171
|
+
if !containsText(numbers, entryType) {
|
|
172
|
+
t.Fatalf("%s literal inline numeric values = %q, want entry type %s", level, numbers, entryType)
|
|
173
|
+
}
|
|
174
|
+
})
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
func TestDynamicLogInlineKeepsSingleMessageEvaluationAndSentinelLaneClear(t *testing.T) {
|
|
179
|
+
block := emittedLogBlock("info", 0)
|
|
180
|
+
identifiers := collectNodeText(block, shimast.KindIdentifier)
|
|
181
|
+
if containsText(identifiers, "_messageTemplateIds") {
|
|
182
|
+
t.Fatalf("dynamic inline unexpectedly writes the template lane: %q", identifiers)
|
|
183
|
+
}
|
|
184
|
+
if !containsText(identifiers, "message_values") || !containsText(identifiers, "message_nulls") {
|
|
185
|
+
t.Fatalf("dynamic inline does not use ordinary message storage: %q", identifiers)
|
|
186
|
+
}
|
|
187
|
+
count := 0
|
|
188
|
+
for _, identifier := range identifiers {
|
|
189
|
+
if identifier == "dynamicMessage" {
|
|
190
|
+
count++
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if count != 1 {
|
|
194
|
+
t.Fatalf("dynamic message evaluation occurrences = %d, want 1", count)
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
func TestFactoryLocalOpsReceiveMetadataTemplatesAndStableChildRewrite(t *testing.T) {
|
|
199
|
+
output := transformTemplateFixture(t, `
|
|
200
|
+
function createFactoryOps() {
|
|
201
|
+
const child = defineOp('factory-child', (ctx) => {
|
|
202
|
+
ctx.log.info('child literal');
|
|
203
|
+
return ctx.ok(null);
|
|
204
|
+
});
|
|
205
|
+
const grouped = defineOps({
|
|
206
|
+
grouped(ctx) { ctx.log.debug('group literal'); return ctx.ok(null); },
|
|
207
|
+
});
|
|
208
|
+
const parent = defineOp('factory-parent', async (ctx) => {
|
|
209
|
+
ctx.log.warn('parent literal');
|
|
210
|
+
await ctx.span('child-call', child);
|
|
211
|
+
return ctx.ok(null);
|
|
212
|
+
});
|
|
213
|
+
return { child, grouped, parent };
|
|
214
|
+
}
|
|
215
|
+
`)
|
|
216
|
+
|
|
217
|
+
for _, literal := range []string{"child literal", "group literal", "parent literal"} {
|
|
218
|
+
if strings.Count(output, literal) != 1 {
|
|
219
|
+
t.Fatalf("%q occurrences = %d, want exactly one metadata entry\n%s", literal, strings.Count(output, literal), output)
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if strings.Count(output, "logTemplateIds") != 3 {
|
|
223
|
+
t.Fatalf("logTemplateIds occurrences = %d, want child, grouped, and parent metadata\n%s", strings.Count(output, "logTemplateIds"), output)
|
|
224
|
+
}
|
|
225
|
+
if !strings.Contains(output, "_messageTemplateIds[$$i] = 1") {
|
|
226
|
+
t.Fatalf("factory-local literal logs did not use template IDs\n%s", output)
|
|
227
|
+
}
|
|
228
|
+
if strings.Count(output, "ctx.span0(") != 1 {
|
|
229
|
+
t.Fatalf("stable child span rewrites = %d, want exactly one\n%s", strings.Count(output, "ctx.span0("), output)
|
|
230
|
+
}
|
|
231
|
+
for _, field := range []string{"child.SpanBufferClass", "child.metadata", "child.fn", "child.runtimeHint"} {
|
|
232
|
+
if !strings.Contains(output, field) {
|
|
233
|
+
t.Fatalf("stable child span output missing %s\n%s", field, output)
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
func TestLiteralLogsInValueContextsUseRecordedIDsWithoutDoubleRewrite(t *testing.T) {
|
|
239
|
+
output := transformTemplateFixture(t, `
|
|
240
|
+
declare function dynamicMessage(): string;
|
|
241
|
+
declare function consume(value: unknown): void;
|
|
242
|
+
defineOp('value-contexts', (ctx) => {
|
|
243
|
+
const initialized = ctx.log.info('initializer literal');
|
|
244
|
+
const conditional = true ? ctx.log.warn('conditional literal') : ctx.log.warn(dynamicMessage());
|
|
245
|
+
consume(ctx.log.error('argument literal'));
|
|
246
|
+
return ctx.ok([initialized, conditional]);
|
|
247
|
+
});
|
|
248
|
+
`)
|
|
249
|
+
|
|
250
|
+
if !strings.Contains(output, `logTemplateIds: ["initializer literal", "conditional literal", "argument literal"]`) {
|
|
251
|
+
t.Fatalf("value-context metadata table missing or out of order\n%s", output)
|
|
252
|
+
}
|
|
253
|
+
for _, call := range []string{"ctx.log._infoTemplate(1)", "ctx.log._warnTemplate(2)", "ctx.log._errorTemplate(3)"} {
|
|
254
|
+
if strings.Count(output, call) != 1 {
|
|
255
|
+
t.Fatalf("%s occurrences = %d, want exactly one\n%s", call, strings.Count(output, call), output)
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
if strings.Count(output, "ctx.log.warn(dynamicMessage())") != 1 {
|
|
259
|
+
t.Fatalf("dynamic log evaluations = %d, want one\n%s", strings.Count(output, "ctx.log.warn(dynamicMessage())"), output)
|
|
260
|
+
}
|
|
261
|
+
if strings.Contains(output, "ctx.log.info('initializer literal')") || strings.Contains(output, "ctx.log.warn('conditional literal')") || strings.Contains(output, "ctx.log.error('argument literal')") {
|
|
262
|
+
t.Fatalf("literal value-context call survived alongside its template rewrite\n%s", output)
|
|
263
|
+
}
|
|
264
|
+
}
|
package/plugin.cjs
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// ttsc plugin descriptor for @smoothbricks/lmao-ttsc.
|
|
2
|
+
//
|
|
3
|
+
// ttsc discovers this through the package's `ttsc.plugin` field when the
|
|
4
|
+
// package is a direct dependency, or through explicit compiler plugin config:
|
|
5
|
+
// { "compilerOptions": { "plugins": [{ "transform": "@smoothbricks/lmao-ttsc" }] } }
|
|
6
|
+
//
|
|
7
|
+
// The Go plugin source lives in ./plugin and is built on the consumer's
|
|
8
|
+
// machine by ttsc (cached by ttsc version, tsgo version, platform, and
|
|
9
|
+
// plugin source hash). Bun build and runtime hosts invoke this same native
|
|
10
|
+
// implementation through @ttsc/unplugin; there is no parallel JS transformer.
|
|
11
|
+
const path = require('node:path');
|
|
12
|
+
|
|
13
|
+
module.exports = function createLmaoTtscPlugin(context) {
|
|
14
|
+
return {
|
|
15
|
+
name: '@smoothbricks/lmao-ttsc',
|
|
16
|
+
source: path.resolve(context.dirname, 'plugin'),
|
|
17
|
+
stage: 'transform',
|
|
18
|
+
};
|
|
19
|
+
};
|
package/plugin.d.cts
ADDED
package/README.md
DELETED