bare-script 3.7.6 → 3.8.1
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/lib/bare.js +62 -39
- package/lib/include/args.bare +0 -7
- package/lib/include/dataTable.bare +0 -7
- package/lib/include/diff.bare +1 -8
- package/lib/include/forms.bare +0 -7
- package/lib/include/markdownUp.bare +0 -8
- package/lib/include/pager.bare +0 -7
- package/lib/include/unittest.bare +63 -39
- package/lib/include/unittestMock.bare +0 -7
- package/lib/library.js +30 -3
- package/lib/model.js +27 -1
- package/lib/runtimeAsync.js +24 -3
- package/package.json +1 -1
package/lib/bare.js
CHANGED
|
@@ -8,6 +8,7 @@ import {executeScriptAsync} from './runtimeAsync.js';
|
|
|
8
8
|
import {fileURLToPath} from 'url';
|
|
9
9
|
import {lintScript} from './model.js';
|
|
10
10
|
import {readFile} from 'fs/promises';
|
|
11
|
+
import {systemGlobalIncludesName} from './library.js';
|
|
11
12
|
import {urlFileRelative} from './options.js';
|
|
12
13
|
import {valueBoolean} from './value.js';
|
|
13
14
|
|
|
@@ -55,12 +56,20 @@ export async function main(options) {
|
|
|
55
56
|
|
|
56
57
|
// Get the scripts to run
|
|
57
58
|
let {scripts} = args;
|
|
59
|
+
let ixUserScript = 0;
|
|
58
60
|
if (args.markdownUp) {
|
|
59
61
|
scripts = [['code', 'include <markdownUp.bare>'], ...scripts];
|
|
62
|
+
ixUserScript = 1;
|
|
63
|
+
|
|
64
|
+
// Add unittest.bare argument globals
|
|
65
|
+
globals.vUnittestReport = true;
|
|
66
|
+
if (args.static) {
|
|
67
|
+
globals.vUnittestDisabled = true;
|
|
68
|
+
}
|
|
60
69
|
}
|
|
61
70
|
|
|
62
71
|
// Parse and execute all source files in order
|
|
63
|
-
for (const [scriptType, scriptValue] of scripts) {
|
|
72
|
+
for (const [ixScript, [scriptType, scriptValue]] of scripts.entries()) {
|
|
64
73
|
// Get the script source
|
|
65
74
|
let scriptName;
|
|
66
75
|
let scriptSource;
|
|
@@ -80,16 +89,58 @@ export async function main(options) {
|
|
|
80
89
|
}
|
|
81
90
|
} else {
|
|
82
91
|
inlineCount += 1;
|
|
83
|
-
|
|
92
|
+
const inlineDisplay = inlineCount - ixUserScript;
|
|
93
|
+
scriptName = `<string${inlineDisplay > 1 ? inlineDisplay : ''}>`;
|
|
84
94
|
scriptSource = scriptValue;
|
|
85
95
|
}
|
|
86
96
|
|
|
87
97
|
// Parse the script source
|
|
88
98
|
const script = parseScript(scriptSource, 1, scriptName);
|
|
89
99
|
|
|
100
|
+
// Execute?
|
|
101
|
+
let staticGlobals = null;
|
|
102
|
+
if (args.static !== 's') {
|
|
103
|
+
// Set the globals to use for static analysis (below)
|
|
104
|
+
if (!args.static || ixScript < ixUserScript) {
|
|
105
|
+
staticGlobals = globals;
|
|
106
|
+
} else {
|
|
107
|
+
// Copy global to keep each script as isolated as possible
|
|
108
|
+
staticGlobals = {...globals};
|
|
109
|
+
const globalIncludes = staticGlobals[systemGlobalIncludesName] ?? null;
|
|
110
|
+
if (globalIncludes !== null && typeof globalIncludes === 'object') {
|
|
111
|
+
staticGlobals[systemGlobalIncludesName] = {...globalIncludes};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Execute the script
|
|
116
|
+
const timeBegin = performance.now();
|
|
117
|
+
const {fetchFn} = options;
|
|
118
|
+
const fetchIncludeFn = (fetchURL, fetchOptions) => fetchInclude(fetchFn, fetchURL, fetchOptions);
|
|
119
|
+
const result = await executeScriptAsync(script, {
|
|
120
|
+
'debug': args.debug ?? false,
|
|
121
|
+
'fetchFn': fetchIncludeFn,
|
|
122
|
+
'globals': staticGlobals,
|
|
123
|
+
'logFn': options.logFn,
|
|
124
|
+
'systemPrefix': fetchIncludePrefix,
|
|
125
|
+
'urlFn': scriptType === 'file' ? (url) => urlFileRelative(scriptName, url) : null
|
|
126
|
+
|
|
127
|
+
});
|
|
128
|
+
if (Number.isInteger(result) && result >= 0 && result <= 255) {
|
|
129
|
+
statusCode = result || statusCode;
|
|
130
|
+
} else {
|
|
131
|
+
statusCode = (valueBoolean(result) ? 1 : 0) || statusCode;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Log script execution end with timing
|
|
135
|
+
if (args.debug && ixScript >= ixUserScript) {
|
|
136
|
+
const timeEnd = performance.now();
|
|
137
|
+
options.logFn(`BareScript executed in ${(timeEnd - timeBegin).toFixed(1)} milliseconds`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
90
141
|
// Run the bare-script linter?
|
|
91
|
-
if (args.static
|
|
92
|
-
const warnings = lintScript(script);
|
|
142
|
+
if (args.static && ixScript >= ixUserScript) {
|
|
143
|
+
const warnings = lintScript(script, staticGlobals);
|
|
93
144
|
if (warnings.length === 0) {
|
|
94
145
|
options.logFn(`BareScript static analysis "${scriptName}" ... OK`);
|
|
95
146
|
} else {
|
|
@@ -99,43 +150,12 @@ export async function main(options) {
|
|
|
99
150
|
for (const warning of warnings) {
|
|
100
151
|
options.logFn(warning);
|
|
101
152
|
}
|
|
102
|
-
|
|
103
|
-
statusCode = 1;
|
|
104
|
-
break;
|
|
105
|
-
}
|
|
153
|
+
statusCode = 1;
|
|
106
154
|
}
|
|
107
155
|
}
|
|
108
|
-
if (args.static) {
|
|
109
|
-
continue;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Execute the script
|
|
113
|
-
const timeBegin = performance.now();
|
|
114
|
-
const {fetchFn} = options;
|
|
115
|
-
const fetchIncludeFn = (fetchURL, fetchOptions) => fetchInclude(fetchFn, fetchURL, fetchOptions);
|
|
116
|
-
const result = await executeScriptAsync(script, {
|
|
117
|
-
'debug': args.debug ?? false,
|
|
118
|
-
'fetchFn': fetchIncludeFn,
|
|
119
|
-
'globals': globals,
|
|
120
|
-
'logFn': options.logFn,
|
|
121
|
-
'systemPrefix': fetchIncludePrefix,
|
|
122
|
-
'urlFn': scriptType === 'file' ? (url) => urlFileRelative(scriptName, url) : null
|
|
123
|
-
|
|
124
|
-
});
|
|
125
|
-
if (Number.isInteger(result) && result >= 0 && result <= 255) {
|
|
126
|
-
statusCode = result;
|
|
127
|
-
} else {
|
|
128
|
-
statusCode = valueBoolean(result) ? 1 : 0;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// Log script execution end with timing
|
|
132
|
-
if (args.debug) {
|
|
133
|
-
const timeEnd = performance.now();
|
|
134
|
-
options.logFn(`BareScript executed in ${(timeEnd - timeBegin).toFixed(1)} milliseconds`);
|
|
135
|
-
}
|
|
136
156
|
|
|
137
157
|
// Stop on error status code
|
|
138
|
-
if (statusCode !== 0) {
|
|
158
|
+
if (statusCode !== 0 && !args.static) {
|
|
139
159
|
break;
|
|
140
160
|
}
|
|
141
161
|
}
|
|
@@ -202,7 +222,9 @@ export function parseArgs(argv) {
|
|
|
202
222
|
} else if (arg === '-m' || arg === '--markdown-up') {
|
|
203
223
|
args.markdownUp = true;
|
|
204
224
|
} else if (arg === '-s' || arg === '--static') {
|
|
205
|
-
args.static =
|
|
225
|
+
args.static = 's';
|
|
226
|
+
} else if (arg === '-x' || arg === '--staticx') {
|
|
227
|
+
args.static = 'x';
|
|
206
228
|
} else if (arg === '-v' || arg === '--var') {
|
|
207
229
|
if (iArg + 2 >= argv.length) {
|
|
208
230
|
throw new Error('argument -v/--var: expected 2 arguments');
|
|
@@ -222,7 +244,7 @@ export function parseArgs(argv) {
|
|
|
222
244
|
|
|
223
245
|
// The command-line interface (CLI) help text
|
|
224
246
|
export const helpText = `\
|
|
225
|
-
usage: bare [-h] [-c CODE] [-d] [-m] [-s] [-v VAR EXPR] [file ...]
|
|
247
|
+
usage: bare [-h] [-c CODE] [-d] [-m] [-s] [-x] [-v VAR EXPR] [file ...]
|
|
226
248
|
|
|
227
249
|
The BareScript command-line interface
|
|
228
250
|
|
|
@@ -235,4 +257,5 @@ options:
|
|
|
235
257
|
-d, --debug enable debug mode
|
|
236
258
|
-m, --markdown-up run with MarkdownUp stubs
|
|
237
259
|
-s, --static perform static analysis
|
|
260
|
+
-x, --staticx perform static analysis with execution
|
|
238
261
|
-v, --var VAR EXPR set a global variable to an expression value`;
|
package/lib/include/args.bare
CHANGED
|
@@ -2,13 +2,6 @@
|
|
|
2
2
|
# https://github.com/craigahobbs/markdown-up/blob/main/LICENSE
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
# Include sentinel
|
|
6
|
-
if systemGlobalGet('argsSentinel'):
|
|
7
|
-
return
|
|
8
|
-
endif
|
|
9
|
-
argsSentinel = true
|
|
10
|
-
|
|
11
|
-
|
|
12
5
|
# The URL arguments model
|
|
13
6
|
argsTypes = schemaParse( \
|
|
14
7
|
'group "args.bare"', \
|
|
@@ -2,13 +2,6 @@
|
|
|
2
2
|
# https://github.com/craigahobbs/markdown-up/blob/main/LICENSE
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
# Include sentinel
|
|
6
|
-
if systemGlobalGet('dataTableSentinel'):
|
|
7
|
-
return
|
|
8
|
-
endif
|
|
9
|
-
dataTableSentinel = true
|
|
10
|
-
|
|
11
|
-
|
|
12
5
|
# The data table model's Schema Markdown
|
|
13
6
|
dataTableTypes = schemaParse( \
|
|
14
7
|
'group "Data Table"', \
|
package/lib/include/diff.bare
CHANGED
|
@@ -2,13 +2,6 @@
|
|
|
2
2
|
# https://github.com/craigahobbs/markdown-up/blob/main/LICENSE
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
# Include sentinel
|
|
6
|
-
if systemGlobalGet('diffSentinel'):
|
|
7
|
-
return
|
|
8
|
-
endif
|
|
9
|
-
diffSentinel = true
|
|
10
|
-
|
|
11
|
-
|
|
12
5
|
# The text line difference model
|
|
13
6
|
diffTypes = schemaParse( \
|
|
14
7
|
'group "diff.bare"', \
|
|
@@ -104,7 +97,7 @@ function diffLines(left, right):
|
|
|
104
97
|
endif
|
|
105
98
|
|
|
106
99
|
# Look ahead to find next matching point
|
|
107
|
-
foundMatch =
|
|
100
|
+
foundMatch = false
|
|
108
101
|
ixLeftTmp = ixLeft
|
|
109
102
|
while ixLeftTmp < leftLength:
|
|
110
103
|
ixRightTmp = ixRight
|
package/lib/include/forms.bare
CHANGED
|
@@ -2,13 +2,6 @@
|
|
|
2
2
|
# https://github.com/craigahobbs/markdown-up/blob/main/LICENSE
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
# Include sentinel
|
|
6
|
-
if systemGlobalGet('formsSentinel'):
|
|
7
|
-
return
|
|
8
|
-
endif
|
|
9
|
-
formsSentinel = true
|
|
10
|
-
|
|
11
|
-
|
|
12
5
|
# $function: formsTextElements
|
|
13
6
|
# $group: forms.bare
|
|
14
7
|
# $doc: Create a text input [element model](https://github.com/craigahobbs/element-model#readme)
|
package/lib/include/pager.bare
CHANGED
|
@@ -2,69 +2,60 @@
|
|
|
2
2
|
# https://github.com/craigahobbs/markdown-up/blob/main/LICENSE
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
# Include sentinel
|
|
6
|
-
if systemGlobalGet('unittestSentinel'):
|
|
7
|
-
return
|
|
8
|
-
endif
|
|
9
|
-
unittestSentinel = true
|
|
10
|
-
|
|
11
|
-
|
|
12
5
|
include <args.bare>
|
|
13
6
|
include <dataTable.bare>
|
|
14
7
|
include <diff.bare>
|
|
15
8
|
|
|
16
9
|
|
|
17
|
-
# Test statistics
|
|
18
|
-
unittestTests = {}
|
|
19
|
-
unittestWarnings = []
|
|
20
|
-
|
|
21
|
-
|
|
22
10
|
# $function: unittestRunTest
|
|
23
11
|
# $group: unittest.bare
|
|
24
12
|
# $doc: Run a unit test
|
|
25
13
|
# $arg testName: The test function name
|
|
26
14
|
async function unittestRunTest(testName):
|
|
27
|
-
|
|
28
|
-
if
|
|
29
|
-
|
|
30
|
-
systemGlobalSet('unittestTestName', null)
|
|
15
|
+
# Tests disabled?
|
|
16
|
+
if systemGlobalGet('vUnittestDisabled'):
|
|
17
|
+
return
|
|
31
18
|
endif
|
|
32
|
-
endfunction
|
|
33
19
|
|
|
34
|
-
|
|
35
|
-
# For backward compatibility
|
|
36
|
-
async function unittestRunTestAsync(testName):
|
|
37
|
-
systemLogDebug('unittest.bare: unittestRunTestAsync is deprecated - use unittestRunTest')
|
|
38
|
-
return unittestRunTest(testName)
|
|
39
|
-
endfunction
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
# unittestRunTest helper function
|
|
43
|
-
function unittestRunTestHelper(testName):
|
|
44
20
|
# Single test argument?
|
|
21
|
+
vUnittestTest = systemGlobalGet('vUnittestTest')
|
|
45
22
|
if vUnittestTest && vUnittestTest != testName:
|
|
46
|
-
return
|
|
23
|
+
return
|
|
47
24
|
endif
|
|
48
25
|
|
|
26
|
+
# Get the global unittest data
|
|
27
|
+
unittestData = unittestDataGet()
|
|
28
|
+
unittestTests = objectGet(unittestData, 'tests')
|
|
29
|
+
unittestWarnings = objectGet(unittestData, 'warnings')
|
|
30
|
+
|
|
49
31
|
# Test run multiple times?
|
|
50
32
|
if objectHas(unittestTests, testName):
|
|
51
33
|
arrayPush(unittestWarnings, 'Test "' + testName + '" run multiple times')
|
|
52
|
-
return
|
|
34
|
+
return
|
|
53
35
|
endif
|
|
54
36
|
|
|
55
37
|
# Get the test func
|
|
56
38
|
testFn = systemGlobalGet(testName)
|
|
57
39
|
if !testFn:
|
|
58
40
|
arrayPush(unittestWarnings, 'Test "' + testName + '" not found')
|
|
59
|
-
return
|
|
41
|
+
return
|
|
60
42
|
endif
|
|
61
43
|
|
|
62
44
|
# Add the unit test result array
|
|
63
45
|
testFailures = []
|
|
64
46
|
objectSet(unittestTests, testName, testFailures)
|
|
65
|
-
systemGlobalSet('unittestTestName', testName)
|
|
66
47
|
|
|
67
|
-
|
|
48
|
+
# Run the test
|
|
49
|
+
objectSet(unittestData, 'current', testName)
|
|
50
|
+
testFn()
|
|
51
|
+
objectSet(unittestData, 'current', null)
|
|
52
|
+
endfunction
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# For backward compatibility
|
|
56
|
+
async function unittestRunTestAsync(testName):
|
|
57
|
+
systemLogDebug('unittest.bare: unittestRunTestAsync is deprecated - use unittestRunTest')
|
|
58
|
+
return unittestRunTest(testName)
|
|
68
59
|
endfunction
|
|
69
60
|
|
|
70
61
|
|
|
@@ -79,8 +70,13 @@ function unittestEqual(actual, expected, description):
|
|
|
79
70
|
return
|
|
80
71
|
endif
|
|
81
72
|
|
|
73
|
+
# Get the global unittest data
|
|
74
|
+
unittestData = unittestDataGet()
|
|
75
|
+
unittestTests = objectGet(unittestData, 'tests')
|
|
76
|
+
unittestCurrent = objectGet(unittestData, 'current')
|
|
77
|
+
testFailures = objectGet(unittestTests, unittestCurrent)
|
|
78
|
+
|
|
82
79
|
# Add the test failure error lines
|
|
83
|
-
testFailures = objectGet(unittestTests, unittestTestName)
|
|
84
80
|
errorLines = [ \
|
|
85
81
|
'Equal:', \
|
|
86
82
|
'', \
|
|
@@ -143,8 +139,13 @@ function unittestDeepEqual(actual, expected, description):
|
|
|
143
139
|
arrayPush(errorLines, '```')
|
|
144
140
|
endif
|
|
145
141
|
|
|
142
|
+
# Get the global unittest data
|
|
143
|
+
unittestData = unittestDataGet()
|
|
144
|
+
unittestTests = objectGet(unittestData, 'tests')
|
|
145
|
+
unittestCurrent = objectGet(unittestData, 'current')
|
|
146
|
+
testFailures = objectGet(unittestTests, unittestCurrent)
|
|
147
|
+
|
|
146
148
|
# Add the test failure error lines
|
|
147
|
-
testFailures = objectGet(unittestTests, unittestTestName)
|
|
148
149
|
if description:
|
|
149
150
|
arrayPush(testFailures, arrayExtend([markdownEscape(description), ''], errorLines))
|
|
150
151
|
else:
|
|
@@ -153,6 +154,17 @@ function unittestDeepEqual(actual, expected, description):
|
|
|
153
154
|
endfunction
|
|
154
155
|
|
|
155
156
|
|
|
157
|
+
# Helper to get the global unittest data object
|
|
158
|
+
function unittestDataGet():
|
|
159
|
+
unittestData = systemGlobalGet('unittestData')
|
|
160
|
+
if unittestData == null:
|
|
161
|
+
unittestData = {'tests': {}, 'warnings': [], 'current': null}
|
|
162
|
+
systemGlobalSet('unittestData', unittestData)
|
|
163
|
+
endif
|
|
164
|
+
return unittestData
|
|
165
|
+
endfunction
|
|
166
|
+
|
|
167
|
+
|
|
156
168
|
# $function: unittestReport
|
|
157
169
|
# $group: unittest.bare
|
|
158
170
|
# $doc: Render the unit test report
|
|
@@ -171,10 +183,16 @@ function unittestReport(options):
|
|
|
171
183
|
|
|
172
184
|
# Parse arguments
|
|
173
185
|
args = argsParse(unittestReportArguments)
|
|
174
|
-
|
|
175
|
-
scriptName = objectGet(args, 'script')
|
|
186
|
+
isDisabled = objectGet(args, 'disabled')
|
|
176
187
|
hideTests = objectGet(args, 'hideTests')
|
|
177
188
|
isReport = objectGet(args, 'report')
|
|
189
|
+
scriptName = objectGet(args, 'script')
|
|
190
|
+
testNameArg = objectGet(args, 'test')
|
|
191
|
+
|
|
192
|
+
# Disabled?
|
|
193
|
+
if isDisabled:
|
|
194
|
+
return 0
|
|
195
|
+
endif
|
|
178
196
|
|
|
179
197
|
# Script coverage details page?
|
|
180
198
|
if scriptName:
|
|
@@ -182,6 +200,11 @@ function unittestReport(options):
|
|
|
182
200
|
return 0
|
|
183
201
|
endif
|
|
184
202
|
|
|
203
|
+
# Get the global unittest data
|
|
204
|
+
unittestData = unittestDataGet()
|
|
205
|
+
unittestTests = objectGet(unittestData, 'tests')
|
|
206
|
+
unittestWarnings = objectGet(unittestData, 'warnings')
|
|
207
|
+
|
|
185
208
|
# Compute test statistics
|
|
186
209
|
testNames = arraySort(objectKeys(unittestTests))
|
|
187
210
|
testCount = arrayLength(testNames)
|
|
@@ -318,10 +341,11 @@ endfunction
|
|
|
318
341
|
|
|
319
342
|
# unittestReport application arguments
|
|
320
343
|
unittestReportArguments = argsValidate([ \
|
|
321
|
-
{'name': '
|
|
322
|
-
{'name': 'script', 'global': 'vUnittestScript'}, \
|
|
344
|
+
{'name': 'disabled', 'global': 'vUnittestDisabled', 'type': 'bool', 'default': false}, \
|
|
323
345
|
{'name': 'hideTests', 'global': 'vUnittestHideTests', 'type': 'bool', 'default': false}, \
|
|
324
|
-
{'name': 'report', 'global': 'vUnittestReport', 'type': 'bool', 'default': false} \
|
|
346
|
+
{'name': 'report', 'global': 'vUnittestReport', 'type': 'bool', 'default': false}, \
|
|
347
|
+
{'name': 'script', 'global': 'vUnittestScript'}, \
|
|
348
|
+
{'name': 'test', 'global': 'vUnittestTest'} \
|
|
325
349
|
])
|
|
326
350
|
|
|
327
351
|
|
|
@@ -2,13 +2,6 @@
|
|
|
2
2
|
# https://github.com/craigahobbs/markdown-up/blob/main/LICENSE
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
# Include sentinel
|
|
6
|
-
if systemGlobalGet('unittestMockSentinel'):
|
|
7
|
-
return
|
|
8
|
-
endif
|
|
9
|
-
unittestMockSentinel = true
|
|
10
|
-
|
|
11
|
-
|
|
12
5
|
# Constants
|
|
13
6
|
unittestMockPixelsPerPoint = 4 / 3
|
|
14
7
|
unittestMockDefaultFontSizePx = 12 * unittestMockPixelsPerPoint
|
package/lib/library.js
CHANGED
|
@@ -82,14 +82,16 @@ const arrayExtendArgs = valueArgsModel([
|
|
|
82
82
|
// $group: Array
|
|
83
83
|
// $doc: Flat an array hierarchy
|
|
84
84
|
// $arg array: The array to flat
|
|
85
|
+
// $arg depth: The maximum depth of the array hierarchy
|
|
85
86
|
// $return: The flated array
|
|
86
87
|
function arrayFlat(args) {
|
|
87
|
-
const [array] = valueArgsValidate(arrayFlatArgs, args);
|
|
88
|
-
return array.flat(
|
|
88
|
+
const [array, depth] = valueArgsValidate(arrayFlatArgs, args);
|
|
89
|
+
return array.flat(depth);
|
|
89
90
|
}
|
|
90
91
|
|
|
91
92
|
const arrayFlatArgs = valueArgsModel([
|
|
92
|
-
{'name': 'array', 'type': 'array'}
|
|
93
|
+
{'name': 'array', 'type': 'array'},
|
|
94
|
+
{'name': 'depth', 'type': 'number', 'integer': true, 'default': 10}
|
|
93
95
|
]);
|
|
94
96
|
|
|
95
97
|
|
|
@@ -1987,6 +1989,29 @@ struct SystemFetchRequest
|
|
|
1987
1989
|
`);
|
|
1988
1990
|
|
|
1989
1991
|
|
|
1992
|
+
// System includes object global variable name
|
|
1993
|
+
export const systemGlobalIncludesName = '__bareScriptIncludes';
|
|
1994
|
+
|
|
1995
|
+
|
|
1996
|
+
// $function: systemGlobalIncludesGet
|
|
1997
|
+
// $group: System
|
|
1998
|
+
// $doc: Get the global system includes object
|
|
1999
|
+
// $return: The global system includes object
|
|
2000
|
+
function systemGlobalIncludesGet(unusedArgs, options) {
|
|
2001
|
+
const globals = (options !== null ? (options.globals ?? null) : null);
|
|
2002
|
+
return (globals !== null ? (globals[systemGlobalIncludesName] ?? null) : null);
|
|
2003
|
+
}
|
|
2004
|
+
|
|
2005
|
+
|
|
2006
|
+
// $function: systemGlobalIncludesName
|
|
2007
|
+
// $group: System
|
|
2008
|
+
// $doc: Get the system includes object global variable name
|
|
2009
|
+
// $return: The system includes object global variable name
|
|
2010
|
+
function systemGlobalIncludesNameFn() {
|
|
2011
|
+
return systemGlobalIncludesName;
|
|
2012
|
+
}
|
|
2013
|
+
|
|
2014
|
+
|
|
1990
2015
|
// $function: systemGlobalGet
|
|
1991
2016
|
// $group: System
|
|
1992
2017
|
// $doc: Get a global variable value
|
|
@@ -2248,6 +2273,8 @@ export const scriptFunctions = {
|
|
|
2248
2273
|
systemCompare,
|
|
2249
2274
|
systemFetch,
|
|
2250
2275
|
systemGlobalGet,
|
|
2276
|
+
systemGlobalIncludesGet,
|
|
2277
|
+
'systemGlobalIncludesName': systemGlobalIncludesNameFn,
|
|
2251
2278
|
systemGlobalSet,
|
|
2252
2279
|
systemIs,
|
|
2253
2280
|
systemLog,
|
package/lib/model.js
CHANGED
|
@@ -320,9 +320,10 @@ export function validateExpression(expr) {
|
|
|
320
320
|
* Lint a BareScript script model
|
|
321
321
|
*
|
|
322
322
|
* @param {Object} script - The [BareScript model](./model/#var.vName='BareScript')
|
|
323
|
+
* @param {?Object} globals - The script global variables
|
|
323
324
|
* @returns {string[]} The array of lint warnings
|
|
324
325
|
*/
|
|
325
|
-
export function lintScript(script) {
|
|
326
|
+
export function lintScript(script, globals = null) {
|
|
326
327
|
const warnings = [];
|
|
327
328
|
const {statements} = script;
|
|
328
329
|
|
|
@@ -341,6 +342,15 @@ export function lintScript(script) {
|
|
|
341
342
|
}
|
|
342
343
|
}
|
|
343
344
|
|
|
345
|
+
// Unknown global variable?
|
|
346
|
+
if (globals !== null) {
|
|
347
|
+
for (const varName of Object.keys(varUses).sort()) {
|
|
348
|
+
if (!(varName in varAssigns) && !(varName in globals) && !builtinGlobals.has(varName)) {
|
|
349
|
+
lintScriptWarning(warnings, script, statements[varUses[varName]], `Unknown global variable "${varName}"`);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
344
354
|
// Iterate global statements
|
|
345
355
|
const functionsDefined = {};
|
|
346
356
|
const labelsDefined = {};
|
|
@@ -386,6 +396,18 @@ export function lintScript(script) {
|
|
|
386
396
|
}
|
|
387
397
|
}
|
|
388
398
|
|
|
399
|
+
// Unknown global variable?
|
|
400
|
+
if (globals !== null) {
|
|
401
|
+
for (const varName of Object.keys(fnVarUses).sort()) {
|
|
402
|
+
if (!(varName in fnVarAssigns) && (args === null || args.indexOf(varName) === -1) &&
|
|
403
|
+
!(varName in globals) && !builtinGlobals.has(varName)) {
|
|
404
|
+
lintScriptWarning(
|
|
405
|
+
warnings, script, fnStatements[fnVarUses[varName]], `Unknown global variable "${varName}"`
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
389
411
|
// Function argument checks
|
|
390
412
|
if (args !== null) {
|
|
391
413
|
const argsDefined = new Set();
|
|
@@ -499,6 +521,10 @@ export function lintScript(script) {
|
|
|
499
521
|
}
|
|
500
522
|
|
|
501
523
|
|
|
524
|
+
// Builtin global variable names
|
|
525
|
+
const builtinGlobals = new Set(['false', 'if', 'null', 'true']);
|
|
526
|
+
|
|
527
|
+
|
|
502
528
|
// Helper to format static analysis warnings
|
|
503
529
|
function lintScriptWarning(warnings, script, statement, message) {
|
|
504
530
|
const scriptName = script.scriptName ?? '';
|
package/lib/runtimeAsync.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import {BareScriptRuntimeError, evaluateExpression, recordStatementCoverage, scriptFunction} from './runtime.js';
|
|
7
7
|
import {ValueArgsError, valueBoolean, valueCompare, valueString} from './value.js';
|
|
8
|
-
import {coverageGlobalName, defaultMaxStatements, expressionFunctions, scriptFunctions} from './library.js';
|
|
8
|
+
import {coverageGlobalName, defaultMaxStatements, expressionFunctions, scriptFunctions, systemGlobalIncludesName} from './library.js';
|
|
9
9
|
import {lintScript} from './model.js';
|
|
10
10
|
import {parseScript} from './parser.js';
|
|
11
11
|
import {urlFileRelative} from './options.js';
|
|
@@ -123,9 +123,9 @@ async function executeScriptHelperAsync(script, statements, options, locals) {
|
|
|
123
123
|
|
|
124
124
|
// Include?
|
|
125
125
|
} else if (statementKey === 'include') {
|
|
126
|
-
//
|
|
126
|
+
// Compute the include script URLs
|
|
127
127
|
const urlFn = options.urlFn ?? null;
|
|
128
|
-
const
|
|
128
|
+
const unfilteredIncludeURLs = statement.include.includes.map(({url, system = false}) => {
|
|
129
129
|
let includeURL;
|
|
130
130
|
if (system && 'systemPrefix' in options) {
|
|
131
131
|
includeURL = urlFileRelative(options.systemPrefix, url);
|
|
@@ -134,6 +134,21 @@ async function executeScriptHelperAsync(script, statements, options, locals) {
|
|
|
134
134
|
}
|
|
135
135
|
return {includeURL, 'systemInclude': system};
|
|
136
136
|
});
|
|
137
|
+
|
|
138
|
+
// Filter already included
|
|
139
|
+
let globalIncludes = globals[systemGlobalIncludesName] ?? null;
|
|
140
|
+
if (globalIncludes === null || typeof globalIncludes !== 'object') {
|
|
141
|
+
globalIncludes = {};
|
|
142
|
+
globals[systemGlobalIncludesName] = globalIncludes;
|
|
143
|
+
}
|
|
144
|
+
const includeURLs = unfilteredIncludeURLs.filter(({includeURL}) => {
|
|
145
|
+
if (globalIncludes[includeURL]) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
return true;
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// Fetch the include script text
|
|
137
152
|
const responses = await Promise.all(includeURLs.map(async ({includeURL, systemInclude}) => {
|
|
138
153
|
try {
|
|
139
154
|
const response = ('fetchFn' in options ? await options.fetchFn(includeURL) : null);
|
|
@@ -160,6 +175,12 @@ async function executeScriptHelperAsync(script, statements, options, locals) {
|
|
|
160
175
|
throw new BareScriptRuntimeError(script, statement, `Include of "${includeURL}" failed`);
|
|
161
176
|
}
|
|
162
177
|
|
|
178
|
+
// Mark as included. Check again if the URL is included.
|
|
179
|
+
if (globalIncludes[includeURL]) {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
globalIncludes[includeURL] = true;
|
|
183
|
+
|
|
163
184
|
// Parse the include script
|
|
164
185
|
const includeScript = parseScript(includeText, 1, includeURL);
|
|
165
186
|
if (systemInclude) {
|