1 | "use strict";
|
2 |
|
3 | Object.defineProperty(exports, "__esModule", {
|
4 | value: true
|
5 | });
|
6 | exports.default = get;
|
7 | exports.multiple = multiple;
|
8 | exports.readFile = readFile;
|
9 | exports.resolveOptionPluginOrPreset = resolveOptionPluginOrPreset;
|
10 | var _semver = require("semver");
|
11 | var _path = require("path");
|
12 | var _fs = require("fs");
|
13 | var _url = require("url");
|
14 | var _module = require("module");
|
15 | const nodeVersion = _semver.clean(process.version.slice(1));
|
16 | function humanize(val, noext) {
|
17 | if (noext) val = _path.basename(val, _path.extname(val));
|
18 | return val.replace(/-/g, " ");
|
19 | }
|
20 | function tryResolve(module) {
|
21 | try {
|
22 | return require.resolve(module);
|
23 | } catch (e) {
|
24 | return null;
|
25 | }
|
26 | }
|
27 | function assertDirectory(loc) {
|
28 | if (!_fs.statSync(loc).isDirectory()) {
|
29 | throw new Error(`Expected ${loc} to be a directory.`);
|
30 | }
|
31 | }
|
32 | function shouldIgnore(name, ignore) {
|
33 | if (ignore && ignore.indexOf(name) >= 0) {
|
34 | return true;
|
35 | }
|
36 | const ext = _path.extname(name);
|
37 | const base = _path.basename(name, ext);
|
38 | return name[0] === "." || ext === ".md" || base === "LICENSE" || base === "options" || name === "package.json";
|
39 | }
|
40 | const EXTENSIONS = [".js", ".mjs", ".ts", ".tsx", ".cts", ".mts", ".vue"];
|
41 | const JSON_AND_EXTENSIONS = [".json", ...EXTENSIONS];
|
42 | function checkFile(loc, allowJSON, matchedLoc) {
|
43 | const ext = _path.extname(loc);
|
44 | const extensions = allowJSON ? JSON_AND_EXTENSIONS : EXTENSIONS;
|
45 | if (!extensions.includes(ext)) {
|
46 | throw new Error(`Unsupported input extension: ${loc}`);
|
47 | }
|
48 | if (!matchedLoc) {
|
49 | return loc;
|
50 | } else {
|
51 | throw new Error(`Found conflicting file matches: ${matchedLoc},${loc}`);
|
52 | }
|
53 | }
|
54 | function pushTask(taskName, taskDir, suite, suiteName) {
|
55 | var _taskOpts$externalHel;
|
56 | const taskDirStats = _fs.statSync(taskDir);
|
57 | let actualLoc, expectLoc, execLoc, execLocAlias, taskOptsLoc, stdoutLoc, stderrLoc, sourceMapLoc, sourceMapVisualLoc, inputSourceMap;
|
58 | const taskOpts = JSON.parse(JSON.stringify(suite.options));
|
59 | if (taskDirStats.isDirectory()) {
|
60 | var _actualLoc, _execLoc, _expectLoc;
|
61 | const files = _fs.readdirSync(taskDir);
|
62 | for (const file of files) {
|
63 | const loc = _path.join(taskDir, file);
|
64 | const name = _path.basename(file, _path.extname(file));
|
65 | switch (name) {
|
66 | case "input":
|
67 | actualLoc = checkFile(loc, false, actualLoc);
|
68 | break;
|
69 | case "exec":
|
70 | execLoc = checkFile(loc, false, execLoc);
|
71 | break;
|
72 | case "output":
|
73 | expectLoc = checkFile(loc, true, expectLoc);
|
74 | break;
|
75 | case "output.extended":
|
76 | expectLoc = checkFile(loc, true, expectLoc);
|
77 | break;
|
78 | case "options":
|
79 | taskOptsLoc = loc;
|
80 | Object.assign(taskOpts, require(taskOptsLoc));
|
81 | break;
|
82 | case "source-map":
|
83 | sourceMapLoc = loc;
|
84 | break;
|
85 | case "source-map-visual":
|
86 | sourceMapVisualLoc = loc;
|
87 | break;
|
88 | case "input-source-map":
|
89 | inputSourceMap = JSON.parse(readFile(loc));
|
90 | break;
|
91 | }
|
92 | }
|
93 | if (files.length > 0 && !actualLoc && !execLoc) {
|
94 | console.warn(`Skipped test folder with invalid layout: ${taskDir}`);
|
95 | return;
|
96 | }
|
97 | (_actualLoc = actualLoc) != null ? _actualLoc : actualLoc = taskDir + "/input.js";
|
98 | (_execLoc = execLoc) != null ? _execLoc : execLoc = taskDir + "/exec.js";
|
99 | (_expectLoc = expectLoc) != null ? _expectLoc : expectLoc = taskDir + "/output.js";
|
100 | stdoutLoc = taskDir + "/stdout.txt";
|
101 | stderrLoc = taskDir + "/stderr.txt";
|
102 | } else if (taskDirStats.isFile()) {
|
103 | const ext = _path.extname(taskDir);
|
104 | if (EXTENSIONS.indexOf(ext) === -1) return;
|
105 | execLoc = taskDir;
|
106 | execLocAlias = suiteName + "/" + taskName;
|
107 | } else {
|
108 | console.warn(`Skipped test folder with invalid layout: ${taskDir}`);
|
109 | return;
|
110 | }
|
111 | const shouldIgnore = taskOpts.BABEL_8_BREAKING === true || taskOpts.SKIP_ON_PUBLISH;
|
112 | if (shouldIgnore) return;
|
113 | function buildTestFile(loc, fileName) {
|
114 | return {
|
115 | loc,
|
116 | code: readFile(loc),
|
117 | filename: !loc ? undefined : fileName === true ? suiteName + "/" + taskName + "/" + _path.basename(loc) : fileName || undefined
|
118 | };
|
119 | }
|
120 | const sourceMapFile = buildTestFile(sourceMapLoc, true);
|
121 | sourceMapFile.code && (sourceMapFile.code = JSON.parse(sourceMapFile.code));
|
122 | const test = {
|
123 | taskDir,
|
124 | optionsDir: taskOptsLoc ? _path.dirname(taskOptsLoc) : null,
|
125 | title: humanize(taskName, true),
|
126 | disabled: taskName[0] === "." ? true : process.env.TEST_babel7plugins_babel8core && taskOpts.SKIP_babel7plugins_babel8core || false,
|
127 | options: taskOpts,
|
128 | doNotSetSourceType: taskOpts.DO_NOT_SET_SOURCE_TYPE,
|
129 | externalHelpers: (_taskOpts$externalHel = taskOpts.externalHelpers) != null ? _taskOpts$externalHel : !!tryResolve("@babel/plugin-external-helpers"),
|
130 | validateLogs: taskOpts.validateLogs,
|
131 | ignoreOutput: taskOpts.ignoreOutput,
|
132 | stdout: buildTestFile(stdoutLoc),
|
133 | stderr: buildTestFile(stderrLoc),
|
134 | exec: buildTestFile(execLoc, execLocAlias),
|
135 | actual: buildTestFile(actualLoc, true),
|
136 | expect: buildTestFile(expectLoc, true),
|
137 | sourceMap: sourceMapFile.code,
|
138 | sourceMapFile,
|
139 | sourceMapVisual: buildTestFile(sourceMapVisualLoc),
|
140 | validateSourceMapVisual: taskOpts.sourceMaps === true || taskOpts.sourceMaps === "both",
|
141 | inputSourceMap
|
142 | };
|
143 | if (test.exec.code && test.actual.code && _path.extname(execLoc) !== _path.extname(actualLoc)) {
|
144 | throw new Error(`Input file extension should match exec file extension: ${execLoc}, ${actualLoc}`);
|
145 | }
|
146 | delete taskOpts.BABEL_8_BREAKING;
|
147 | delete taskOpts.DO_NOT_SET_SOURCE_TYPE;
|
148 | delete taskOpts.SKIP_ON_PUBLISH;
|
149 | delete taskOpts.SKIP_babel7plugins_babel8core;
|
150 | if (taskOpts.minNodeVersion) {
|
151 | const minimumVersion = _semver.clean(taskOpts.minNodeVersion);
|
152 | if (minimumVersion == null) {
|
153 | throw new Error(`'minNodeVersion' has invalid semver format: ${taskOpts.minNodeVersion}`);
|
154 | }
|
155 | if (_semver.lt(nodeVersion, minimumVersion)) {
|
156 | if (test.actual.code) {
|
157 | test.exec.code = null;
|
158 | } else {
|
159 | return;
|
160 | }
|
161 | }
|
162 | delete taskOpts.minNodeVersion;
|
163 | }
|
164 | if (taskOpts.minNodeVersionTransform) {
|
165 | const minimumVersion = _semver.clean(taskOpts.minNodeVersionTransform);
|
166 | if (minimumVersion == null) {
|
167 | throw new Error(`'minNodeVersionTransform' has invalid semver format: ${taskOpts.minNodeVersionTransform}`);
|
168 | }
|
169 | if (_semver.lt(nodeVersion, minimumVersion)) {
|
170 | return;
|
171 | }
|
172 | delete taskOpts.minNodeVersionTransform;
|
173 | }
|
174 | if (taskOpts.os) {
|
175 | let os = taskOpts.os;
|
176 | if (!Array.isArray(os) && typeof os !== "string") {
|
177 | throw new Error(`'os' should be either string or string array: ${taskOpts.os}`);
|
178 | }
|
179 | if (typeof os === "string") {
|
180 | os = [os];
|
181 | }
|
182 | if (!os.includes(process.platform)) {
|
183 | return;
|
184 | }
|
185 | delete taskOpts.os;
|
186 | }
|
187 | suite.tests.push(test);
|
188 | if (taskOpts.throws) {
|
189 | if (test.expect.code) {
|
190 | throw new Error("Test cannot throw and also return output code: " + expectLoc);
|
191 | }
|
192 | if (test.sourceMap) {
|
193 | throw new Error("Test cannot throw and also return sourcemaps: " + sourceMapLoc);
|
194 | }
|
195 | }
|
196 | if (!test.validateLogs && (test.stdout.code || test.stderr.code)) {
|
197 | throw new Error("stdout.txt and stderr.txt are only allowed when the 'validateLogs' option is enabled: " + (test.stdout.code ? stdoutLoc : stderrLoc));
|
198 | }
|
199 | if (test.ignoreOutput) {
|
200 | if (test.expect.code) {
|
201 | throw new Error("Test cannot ignore its output and also validate it: " + expectLoc);
|
202 | }
|
203 | if (!test.validateLogs) {
|
204 | throw new Error("ignoreOutput can only be used when validateLogs is true: " + taskOptsLoc);
|
205 | }
|
206 | }
|
207 | delete test.options.validateLogs;
|
208 | delete test.options.ignoreOutput;
|
209 | delete test.options.externalHelpers;
|
210 | }
|
211 | function wrapPackagesArray(type, names, optionsDir) {
|
212 | return names.map(function (val) {
|
213 | if (typeof val === "string") val = [val];
|
214 | if (val[0][0] === ".") {
|
215 | if (!optionsDir) {
|
216 | throw new Error("Please provide an options.json in test dir when using a " + "relative plugin path.");
|
217 | }
|
218 | val[0] = _path.resolve(optionsDir, val[0]);
|
219 | } else {
|
220 | let name = val[0];
|
221 | const match = name.match(/^(@babel\/(?:plugin-|preset-)?)(.*)$/);
|
222 | if (match) {
|
223 | name = match[2];
|
224 | }
|
225 | const monorepoPath = _path.join(_path.dirname(__filename), "../../..", name.startsWith("codemod") ? "codemods" : "packages", `babel-${type}-${name}/lib/index.js`);
|
226 | if (_fs.existsSync(monorepoPath)) {
|
227 | if (match) {
|
228 | throw new Error(`Remove the "${match[1]}" prefix from "${val[0]}", to load it from the monorepo`);
|
229 | }
|
230 | val[0] = monorepoPath;
|
231 | }
|
232 | }
|
233 | return val;
|
234 | });
|
235 | }
|
236 | function resolveOptionPluginOrPreset(options, optionsDir) {
|
237 | if (options.overrides) {
|
238 | for (const subOption of options.overrides) {
|
239 | resolveOptionPluginOrPreset(subOption, optionsDir);
|
240 | }
|
241 | }
|
242 | if (options.env) {
|
243 | for (const envName in options.env) {
|
244 | if (!hasOwnProperty.call(options.env, envName)) continue;
|
245 | resolveOptionPluginOrPreset(options.env[envName], optionsDir);
|
246 | }
|
247 | }
|
248 | if (options.plugins) {
|
249 | options.plugins = wrapPackagesArray("plugin", options.plugins, optionsDir);
|
250 | }
|
251 | if (options.presets) {
|
252 | options.presets = wrapPackagesArray("preset", options.presets, optionsDir).map(function (val) {
|
253 | if (val.length > 3) {
|
254 | throw new Error("Unexpected extra options " + JSON.stringify(val.slice(3)) + " passed to preset.");
|
255 | }
|
256 | return val;
|
257 | });
|
258 | }
|
259 | return options;
|
260 | }
|
261 | function get(entryLoc) {
|
262 | const suites = [];
|
263 | let rootOpts = {};
|
264 | const rootOptsLoc = tryResolve(entryLoc + "/options");
|
265 | if (rootOptsLoc) rootOpts = require(rootOptsLoc);
|
266 | for (const suiteName of _fs.readdirSync(entryLoc)) {
|
267 | if (shouldIgnore(suiteName)) continue;
|
268 | const suite = {
|
269 | options: Object.assign({}, rootOpts),
|
270 | tests: [],
|
271 | title: humanize(suiteName),
|
272 | filename: entryLoc + "/" + suiteName
|
273 | };
|
274 | assertDirectory(suite.filename);
|
275 | suites.push(suite);
|
276 | const suiteOptsLoc = tryResolve(suite.filename + "/options");
|
277 | if (suiteOptsLoc) {
|
278 | suite.options = resolveOptionPluginOrPreset(require(suiteOptsLoc), suite.filename);
|
279 | }
|
280 | for (const taskName of _fs.readdirSync(suite.filename)) {
|
281 | pushTask(taskName, suite.filename + "/" + taskName, suite, suiteName);
|
282 | }
|
283 | }
|
284 | return suites;
|
285 | }
|
286 | function multiple(entryLoc, ignore) {
|
287 | const categories = {};
|
288 | for (const name of _fs.readdirSync(entryLoc)) {
|
289 | if (shouldIgnore(name, ignore)) continue;
|
290 | const loc = _path.join(entryLoc, name);
|
291 | assertDirectory(loc);
|
292 | categories[name] = get(loc);
|
293 | }
|
294 | return categories;
|
295 | }
|
296 | function readFile(filename) {
|
297 | try {
|
298 | if (filename === undefined) {
|
299 | return "";
|
300 | }
|
301 | return _fs.readFileSync(filename, "utf8").trimRight();
|
302 | } catch (e) {
|
303 | if (e.code === "ENOENT") {
|
304 | return "";
|
305 | }
|
306 | throw e;
|
307 | }
|
308 | }
|
309 |
|
310 |
|