@ttsc/strip 0.7.3 → 0.8.0-dev.20260506
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/README.md +11 -11
- package/go.mod +1 -5
- package/package.json +1 -1
- package/plugin/main.go +7 -9
- package/plugin/strip.go +1 -383
- package/src/index.cjs +1 -1
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
[](https://github.com/samchon/ttsc/tree/master/docs)
|
|
10
10
|
[](https://discord.gg/E94XhzrUCZ)
|
|
11
11
|
|
|
12
|
-
`@ttsc/strip` removes configured call-expression statements and debugger statements from
|
|
12
|
+
`@ttsc/strip` removes configured call-expression statements and debugger statements from TypeScript source before emit.
|
|
13
13
|
|
|
14
14
|
## Setup
|
|
15
15
|
|
|
@@ -28,10 +28,10 @@ Open your project's `tsconfig.json`, then add this entry under `compilerOptions.
|
|
|
28
28
|
{
|
|
29
29
|
"transform": "@ttsc/strip",
|
|
30
30
|
"calls": ["console.log", "console.debug", "assert.*"],
|
|
31
|
-
"statements": ["debugger"]
|
|
32
|
-
}
|
|
33
|
-
]
|
|
34
|
-
}
|
|
31
|
+
"statements": ["debugger"],
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
35
|
}
|
|
36
36
|
```
|
|
37
37
|
|
|
@@ -54,15 +54,15 @@ Call patterns match statement-level calls such as `console.log("debug")` or `ass
|
|
|
54
54
|
// Keep lint first.
|
|
55
55
|
{ "transform": "@ttsc/lint", "config": { "no-var": "error" } },
|
|
56
56
|
|
|
57
|
-
//
|
|
58
|
-
{ "transform": "@ttsc/banner", "banner": "
|
|
57
|
+
// First-party utilities use their documented transform order.
|
|
58
|
+
{ "transform": "@ttsc/banner", "banner": "License MIT" },
|
|
59
59
|
{ "transform": "@ttsc/paths" },
|
|
60
60
|
{
|
|
61
61
|
"transform": "@ttsc/strip",
|
|
62
62
|
"calls": ["console.log", "console.debug", "assert.*"],
|
|
63
|
-
"statements": ["debugger"]
|
|
64
|
-
}
|
|
65
|
-
]
|
|
66
|
-
}
|
|
63
|
+
"statements": ["debugger"],
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
67
|
}
|
|
68
68
|
```
|
package/go.mod
CHANGED
|
@@ -2,11 +2,7 @@ module github.com/samchon/ttsc/packages/strip
|
|
|
2
2
|
|
|
3
3
|
go 1.26
|
|
4
4
|
|
|
5
|
-
require
|
|
6
|
-
github.com/microsoft/typescript-go/shim/ast v0.0.0
|
|
7
|
-
github.com/microsoft/typescript-go/shim/core v0.0.0
|
|
8
|
-
github.com/microsoft/typescript-go/shim/parser v0.0.0
|
|
9
|
-
)
|
|
5
|
+
require github.com/samchon/ttsc/packages/ttsc v0.0.0
|
|
10
6
|
|
|
11
7
|
require (
|
|
12
8
|
github.com/go-json-experiment/json v0.0.0-20260214004413-d219187c3433 // indirect
|
package/package.json
CHANGED
package/plugin/main.go
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
// Native sidecar entrypoint for `@ttsc/strip`.
|
|
2
|
-
//
|
|
3
|
-
// The sidecar implements an output-stage transform for JavaScript emit. It
|
|
4
|
-
// removes configured call statements and optional debugger statements after
|
|
5
|
-
// TypeScript-Go prints JS, leaving typechecking and project loading to ttsc.
|
|
6
2
|
package main
|
|
7
3
|
|
|
8
4
|
import (
|
|
9
5
|
"fmt"
|
|
10
6
|
"os"
|
|
7
|
+
|
|
8
|
+
"github.com/samchon/ttsc/packages/ttsc/utility"
|
|
11
9
|
)
|
|
12
10
|
|
|
13
11
|
const version = "0.0.1"
|
|
@@ -18,19 +16,19 @@ func main() {
|
|
|
18
16
|
|
|
19
17
|
func run(args []string) int {
|
|
20
18
|
if len(args) == 0 {
|
|
21
|
-
fmt.Fprintln(os.Stderr, "@ttsc/strip: command required (expected
|
|
19
|
+
fmt.Fprintln(os.Stderr, "@ttsc/strip: command required (expected build|transform|check|version)")
|
|
22
20
|
return 2
|
|
23
21
|
}
|
|
24
22
|
switch args[0] {
|
|
25
23
|
case "-v", "--version", "version":
|
|
26
24
|
fmt.Fprintf(os.Stdout, "@ttsc/strip %s\n", version)
|
|
27
25
|
return 0
|
|
26
|
+
case "build":
|
|
27
|
+
return utility.RunBuild(args[1:])
|
|
28
|
+
case "transform":
|
|
29
|
+
return utility.RunTransform(args[1:])
|
|
28
30
|
case "check":
|
|
29
|
-
// Strip rewriting is output-only; configuration is validated when output
|
|
30
|
-
// text is actually transformed.
|
|
31
31
|
return 0
|
|
32
|
-
case "output":
|
|
33
|
-
return RunOutput(args[1:])
|
|
34
32
|
default:
|
|
35
33
|
fmt.Fprintf(os.Stderr, "@ttsc/strip: unknown command %q\n", args[0])
|
|
36
34
|
return 2
|
package/plugin/strip.go
CHANGED
|
@@ -1,385 +1,3 @@
|
|
|
1
|
-
// Output transformer for `@ttsc/strip`.
|
|
2
|
-
//
|
|
3
|
-
// The transform parses emitted JavaScript, removes complete statements that
|
|
4
|
-
// match configured call patterns, and preserves all other text. It operates on
|
|
5
|
-
// JS output rather than TS source so the runtime file shape remains consistent
|
|
6
|
-
// with TypeScript-Go's printer.
|
|
7
1
|
package main
|
|
8
2
|
|
|
9
|
-
|
|
10
|
-
"encoding/json"
|
|
11
|
-
"flag"
|
|
12
|
-
"fmt"
|
|
13
|
-
"os"
|
|
14
|
-
"path/filepath"
|
|
15
|
-
"sort"
|
|
16
|
-
"strings"
|
|
17
|
-
|
|
18
|
-
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
19
|
-
shimcore "github.com/microsoft/typescript-go/shim/core"
|
|
20
|
-
shimparser "github.com/microsoft/typescript-go/shim/parser"
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
type pluginEntry struct {
|
|
24
|
-
// Config is the plugin-specific entry from compilerOptions.plugins[] once
|
|
25
|
-
// the sidecar locates the `@ttsc/strip` descriptor in the ordered manifest.
|
|
26
|
-
Config map[string]any `json:"config"`
|
|
27
|
-
Name string `json:"name"`
|
|
28
|
-
Stage string `json:"stage"`
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
type stripTransform struct {
|
|
32
|
-
// calls stores dotted call patterns such as console.log or invariant.*.
|
|
33
|
-
// stripDebugger controls DebuggerStatement removal independently.
|
|
34
|
-
calls []callPattern
|
|
35
|
-
stripDebugger bool
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
type callPattern struct {
|
|
39
|
-
// parts contains the dotted name split on '.'. wildcard means the final
|
|
40
|
-
// source token was '*' and therefore matches any deeper property call.
|
|
41
|
-
parts []string
|
|
42
|
-
wildcard bool
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
type textEdit struct {
|
|
46
|
-
start int
|
|
47
|
-
end int
|
|
48
|
-
text string
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
func RunOutput(args []string) int {
|
|
52
|
-
fs := flag.NewFlagSet("output", flag.ContinueOnError)
|
|
53
|
-
fs.SetOutput(os.Stderr)
|
|
54
|
-
file := fs.String("file", "", "emitted file to transform")
|
|
55
|
-
out := fs.String("out", "", "write transformed text to this file instead of updating --file")
|
|
56
|
-
_ = fs.String("cwd", "", "project directory")
|
|
57
|
-
_ = fs.String("outDir", "", "emit directory override")
|
|
58
|
-
pluginsJSON := fs.String("plugins-json", "", "ttsc plugin manifest JSON")
|
|
59
|
-
_ = fs.String("tsconfig", "tsconfig.json", "project tsconfig")
|
|
60
|
-
if err := fs.Parse(args); err != nil {
|
|
61
|
-
return 2
|
|
62
|
-
}
|
|
63
|
-
if *file == "" {
|
|
64
|
-
fmt.Fprintln(os.Stderr, "@ttsc/strip: output requires --file")
|
|
65
|
-
return 2
|
|
66
|
-
}
|
|
67
|
-
config, err := findConfig(*pluginsJSON)
|
|
68
|
-
if err != nil {
|
|
69
|
-
fmt.Fprintln(os.Stderr, err)
|
|
70
|
-
return 2
|
|
71
|
-
}
|
|
72
|
-
text, err := os.ReadFile(*file)
|
|
73
|
-
if err != nil {
|
|
74
|
-
fmt.Fprintf(os.Stderr, "@ttsc/strip: read %s: %v\n", *file, err)
|
|
75
|
-
return 2
|
|
76
|
-
}
|
|
77
|
-
patched, err := Apply(*file, string(text), config)
|
|
78
|
-
if err != nil {
|
|
79
|
-
fmt.Fprintln(os.Stderr, err)
|
|
80
|
-
return 2
|
|
81
|
-
}
|
|
82
|
-
target := *file
|
|
83
|
-
if *out != "" {
|
|
84
|
-
target = *out
|
|
85
|
-
}
|
|
86
|
-
if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil {
|
|
87
|
-
fmt.Fprintf(os.Stderr, "@ttsc/strip: mkdir: %v\n", err)
|
|
88
|
-
return 2
|
|
89
|
-
}
|
|
90
|
-
if err := os.WriteFile(target, []byte(patched), 0o644); err != nil {
|
|
91
|
-
fmt.Fprintf(os.Stderr, "@ttsc/strip: write %s: %v\n", target, err)
|
|
92
|
-
return 2
|
|
93
|
-
}
|
|
94
|
-
return 0
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
func Apply(fileName string, text string, config map[string]any) (string, error) {
|
|
98
|
-
// Apply is deterministic and has no filesystem side effects. RunOutput owns
|
|
99
|
-
// file reads/writes so unit tests can exercise the transform directly.
|
|
100
|
-
strip, err := parseStrip(config)
|
|
101
|
-
if err != nil {
|
|
102
|
-
return "", err
|
|
103
|
-
}
|
|
104
|
-
return strip.apply(fileName, text)
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
func parseStrip(config map[string]any) (*stripTransform, error) {
|
|
108
|
-
calls, err := stringArrayConfig(config, "calls")
|
|
109
|
-
if err != nil {
|
|
110
|
-
return nil, fmt.Errorf("@ttsc/strip: %w", err)
|
|
111
|
-
}
|
|
112
|
-
statements, err := stringArrayConfig(config, "statements")
|
|
113
|
-
if err != nil {
|
|
114
|
-
return nil, fmt.Errorf("@ttsc/strip: %w", err)
|
|
115
|
-
}
|
|
116
|
-
out := &stripTransform{}
|
|
117
|
-
for _, call := range calls {
|
|
118
|
-
pattern, err := parseCallPattern(call)
|
|
119
|
-
if err != nil {
|
|
120
|
-
return nil, fmt.Errorf("@ttsc/strip: %w", err)
|
|
121
|
-
}
|
|
122
|
-
out.calls = append(out.calls, pattern)
|
|
123
|
-
}
|
|
124
|
-
for _, statement := range statements {
|
|
125
|
-
switch statement {
|
|
126
|
-
case "debugger":
|
|
127
|
-
out.stripDebugger = true
|
|
128
|
-
default:
|
|
129
|
-
return nil, fmt.Errorf("@ttsc/strip: unsupported statement pattern %q", statement)
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
return out, nil
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
func (s *stripTransform) apply(fileName string, text string) (string, error) {
|
|
136
|
-
// Declaration files and maps are intentionally skipped. Strip patterns are
|
|
137
|
-
// JavaScript runtime statements, not type-level declarations.
|
|
138
|
-
if s == nil || !isJavaScriptOutput(fileName) || (len(s.calls) == 0 && !s.stripDebugger) {
|
|
139
|
-
return text, nil
|
|
140
|
-
}
|
|
141
|
-
file := parseJS(fileName, text)
|
|
142
|
-
if file == nil {
|
|
143
|
-
return text, nil
|
|
144
|
-
}
|
|
145
|
-
edits := make([]textEdit, 0)
|
|
146
|
-
// The AST tells us which statements are safe to remove; the original text is
|
|
147
|
-
// still used for editing so comments, whitespace, and unrelated printer
|
|
148
|
-
// details remain untouched.
|
|
149
|
-
var walk func(*shimast.Node)
|
|
150
|
-
walk = func(node *shimast.Node) {
|
|
151
|
-
if node == nil {
|
|
152
|
-
return
|
|
153
|
-
}
|
|
154
|
-
switch node.Kind {
|
|
155
|
-
case shimast.KindDebuggerStatement:
|
|
156
|
-
if s.stripDebugger {
|
|
157
|
-
start, end := statementRemovalRange(text, node)
|
|
158
|
-
edits = append(edits, textEdit{start: start, end: end})
|
|
159
|
-
}
|
|
160
|
-
case shimast.KindExpressionStatement:
|
|
161
|
-
expr := node.AsExpressionStatement().Expression
|
|
162
|
-
name, ok := callExpressionName(expr)
|
|
163
|
-
if ok && s.matchesCall(name) {
|
|
164
|
-
start, end := statementRemovalRange(text, node)
|
|
165
|
-
edits = append(edits, textEdit{start: start, end: end})
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
node.ForEachChild(func(child *shimast.Node) bool {
|
|
169
|
-
walk(child)
|
|
170
|
-
return false
|
|
171
|
-
})
|
|
172
|
-
}
|
|
173
|
-
for _, stmt := range file.Statements.Nodes {
|
|
174
|
-
walk(stmt)
|
|
175
|
-
}
|
|
176
|
-
return applyTextEdits(text, edits), nil
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
func (s *stripTransform) matchesCall(name string) bool {
|
|
180
|
-
for _, pattern := range s.calls {
|
|
181
|
-
if pattern.matches(name) {
|
|
182
|
-
return true
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
return false
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
func parseCallPattern(text string) (callPattern, error) {
|
|
189
|
-
parts := strings.Split(text, ".")
|
|
190
|
-
if len(parts) == 0 {
|
|
191
|
-
return callPattern{}, fmt.Errorf("empty call pattern")
|
|
192
|
-
}
|
|
193
|
-
for i, part := range parts {
|
|
194
|
-
if part == "" {
|
|
195
|
-
return callPattern{}, fmt.Errorf("invalid call pattern %q", text)
|
|
196
|
-
}
|
|
197
|
-
if part == "*" && i != len(parts)-1 {
|
|
198
|
-
// Only terminal wildcards have a clear statement-level meaning:
|
|
199
|
-
// logger.* matches logger.info(), logger.warn(), etc.
|
|
200
|
-
return callPattern{}, fmt.Errorf("wildcard is only supported at the end of call pattern %q", text)
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
wildcard := parts[len(parts)-1] == "*"
|
|
204
|
-
if wildcard {
|
|
205
|
-
parts = parts[:len(parts)-1]
|
|
206
|
-
}
|
|
207
|
-
return callPattern{parts: parts, wildcard: wildcard}, nil
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
func (p callPattern) matches(name string) bool {
|
|
211
|
-
parts := strings.Split(name, ".")
|
|
212
|
-
if p.wildcard {
|
|
213
|
-
if len(parts) <= len(p.parts) {
|
|
214
|
-
return false
|
|
215
|
-
}
|
|
216
|
-
return equalStringSlices(parts[:len(p.parts)], p.parts)
|
|
217
|
-
}
|
|
218
|
-
return equalStringSlices(parts, p.parts)
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
func callExpressionName(expr *shimast.Node) (string, bool) {
|
|
222
|
-
if expr == nil || expr.Kind != shimast.KindCallExpression {
|
|
223
|
-
return "", false
|
|
224
|
-
}
|
|
225
|
-
call := expr.AsCallExpression()
|
|
226
|
-
return dottedName(call.Expression)
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
func dottedName(expr *shimast.Node) (string, bool) {
|
|
230
|
-
if expr == nil {
|
|
231
|
-
return "", false
|
|
232
|
-
}
|
|
233
|
-
switch expr.Kind {
|
|
234
|
-
case shimast.KindIdentifier:
|
|
235
|
-
return expr.Text(), true
|
|
236
|
-
case shimast.KindPropertyAccessExpression:
|
|
237
|
-
prop := expr.AsPropertyAccessExpression()
|
|
238
|
-
left, ok := dottedName(prop.Expression)
|
|
239
|
-
if !ok || prop.Name() == nil {
|
|
240
|
-
return "", false
|
|
241
|
-
}
|
|
242
|
-
return left + "." + prop.Name().Text(), true
|
|
243
|
-
default:
|
|
244
|
-
return "", false
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
func parseJS(fileName string, text string) *shimast.SourceFile {
|
|
249
|
-
normalized := filepath.ToSlash(fileName)
|
|
250
|
-
if !filepath.IsAbs(normalized) {
|
|
251
|
-
if abs, err := filepath.Abs(normalized); err == nil {
|
|
252
|
-
normalized = filepath.ToSlash(abs)
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
opts := shimast.SourceFileParseOptions{FileName: normalized}
|
|
256
|
-
return shimparser.ParseSourceFile(opts, text, shimcore.ScriptKindJS)
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
func stringArrayConfig(config map[string]any, key string) ([]string, error) {
|
|
260
|
-
raw, ok := config[key]
|
|
261
|
-
if !ok || raw == nil {
|
|
262
|
-
return nil, nil
|
|
263
|
-
}
|
|
264
|
-
values, ok := raw.([]any)
|
|
265
|
-
if !ok {
|
|
266
|
-
return nil, fmt.Errorf("%q must be an array of strings", key)
|
|
267
|
-
}
|
|
268
|
-
out := make([]string, 0, len(values))
|
|
269
|
-
for i, value := range values {
|
|
270
|
-
text, ok := value.(string)
|
|
271
|
-
if !ok || strings.TrimSpace(text) == "" {
|
|
272
|
-
return nil, fmt.Errorf("%q[%d] must be a non-empty string", key, i)
|
|
273
|
-
}
|
|
274
|
-
out = append(out, text)
|
|
275
|
-
}
|
|
276
|
-
return out, nil
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
func statementRemovalRange(text string, node *shimast.Node) (int, int) {
|
|
280
|
-
// Remove the whole physical statement line when the statement starts after
|
|
281
|
-
// indentation. This keeps blank-line churn small for common one-statement
|
|
282
|
-
// debug calls.
|
|
283
|
-
start := clamp(node.Pos(), 0, len(text))
|
|
284
|
-
end := clamp(node.End(), start, len(text))
|
|
285
|
-
lineStart := start
|
|
286
|
-
for lineStart > 0 && text[lineStart-1] != '\n' && text[lineStart-1] != '\r' {
|
|
287
|
-
lineStart--
|
|
288
|
-
}
|
|
289
|
-
if strings.TrimSpace(text[lineStart:start]) == "" {
|
|
290
|
-
start = lineStart
|
|
291
|
-
}
|
|
292
|
-
if end < len(text) && text[end] == ';' {
|
|
293
|
-
end++
|
|
294
|
-
}
|
|
295
|
-
for end < len(text) && (text[end] == ' ' || text[end] == '\t') {
|
|
296
|
-
end++
|
|
297
|
-
}
|
|
298
|
-
if end < len(text) && text[end] == '\r' {
|
|
299
|
-
end++
|
|
300
|
-
}
|
|
301
|
-
if end < len(text) && text[end] == '\n' {
|
|
302
|
-
end++
|
|
303
|
-
}
|
|
304
|
-
return start, end
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
func applyTextEdits(text string, edits []textEdit) string {
|
|
308
|
-
if len(edits) == 0 {
|
|
309
|
-
return text
|
|
310
|
-
}
|
|
311
|
-
sort.SliceStable(edits, func(i, j int) bool {
|
|
312
|
-
if edits[i].start == edits[j].start {
|
|
313
|
-
return edits[i].end > edits[j].end
|
|
314
|
-
}
|
|
315
|
-
return edits[i].start > edits[j].start
|
|
316
|
-
})
|
|
317
|
-
out := text
|
|
318
|
-
lastStart := len(text) + 1
|
|
319
|
-
for _, edit := range edits {
|
|
320
|
-
if edit.start < 0 || edit.end < edit.start || edit.start > len(out) {
|
|
321
|
-
continue
|
|
322
|
-
}
|
|
323
|
-
if edit.end > lastStart {
|
|
324
|
-
// Nested edits are collapsed by letting the earlier source range own the
|
|
325
|
-
// bytes. This keeps repeated rule matches from corrupting offsets.
|
|
326
|
-
edit.end = lastStart
|
|
327
|
-
}
|
|
328
|
-
if edit.end > len(out) {
|
|
329
|
-
edit.end = len(out)
|
|
330
|
-
}
|
|
331
|
-
out = out[:edit.start] + edit.text + out[edit.end:]
|
|
332
|
-
lastStart = edit.start
|
|
333
|
-
}
|
|
334
|
-
return out
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
func findConfig(pluginsJSON string) (map[string]any, error) {
|
|
338
|
-
if strings.TrimSpace(pluginsJSON) == "" {
|
|
339
|
-
return nil, fmt.Errorf("@ttsc/strip: missing --plugins-json")
|
|
340
|
-
}
|
|
341
|
-
var entries []pluginEntry
|
|
342
|
-
if err := json.Unmarshal([]byte(pluginsJSON), &entries); err != nil {
|
|
343
|
-
return nil, fmt.Errorf("@ttsc/strip: invalid --plugins-json: %w", err)
|
|
344
|
-
}
|
|
345
|
-
for _, entry := range entries {
|
|
346
|
-
if entry.Name == "@ttsc/strip" {
|
|
347
|
-
if entry.Config == nil {
|
|
348
|
-
return map[string]any{}, nil
|
|
349
|
-
}
|
|
350
|
-
return entry.Config, nil
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
return nil, fmt.Errorf("@ttsc/strip: plugin entry not found")
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
func isJavaScriptOutput(fileName string) bool {
|
|
357
|
-
switch strings.ToLower(filepath.Ext(fileName)) {
|
|
358
|
-
case ".js", ".mjs", ".cjs":
|
|
359
|
-
return true
|
|
360
|
-
default:
|
|
361
|
-
return false
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
func equalStringSlices(a []string, b []string) bool {
|
|
366
|
-
if len(a) != len(b) {
|
|
367
|
-
return false
|
|
368
|
-
}
|
|
369
|
-
for i := range a {
|
|
370
|
-
if a[i] != b[i] {
|
|
371
|
-
return false
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
return true
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
func clamp(value int, min int, max int) int {
|
|
378
|
-
if value < min {
|
|
379
|
-
return min
|
|
380
|
-
}
|
|
381
|
-
if value > max {
|
|
382
|
-
return max
|
|
383
|
-
}
|
|
384
|
-
return value
|
|
385
|
-
}
|
|
3
|
+
// Strip transform logic lives in github.com/samchon/ttsc/packages/ttsc/utility.
|