feat(ts-migrate): migrate sass-compile.js and test-evaluator.js to ts (#43145)
* feat(ts-migrate): rename sass-compile.js to ts * feat(ts-migrate): rename test-evaluator.js to ts * feat(ts-migrate): add webworker in client tsconfig * fix(ts-migrate): fix errors in sass-compile.ts * chore(ts-migrate): install chai types in client * fix(ts-migrate): fix errors in test-evaluator.ts * fix(ts-migrate): rename extensions in webpack-worker * fix(ts-migrate): separate tsconfig for workers * fix(ts-migrate): add worker tsconfig to parser options * chore(ts-migrate): remove unnecessary comment Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com> * fix(ts-migrate): use let instead of const Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com> * fix(ts-migrate): fix eslint errors in sass-compile.ts * fix(ts-migrate): fix eslint errors in test-evaluator.ts * chore(ts-migrate): use unknown instead of generics * chore(ts-migrate): revert worker tsconfig * chore(ts-migrate): add libs in client tsconfig * fix(ts-migrate): use ctx alias in test-evaluator.ts * fix(ts-migrate): use ctx alias in sass-compile.ts * chore: fix errors Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
This commit is contained in:
6
client/package-lock.json
generated
6
client/package-lock.json
generated
@ -5013,6 +5013,12 @@
|
|||||||
"@types/responselike": "*"
|
"@types/responselike": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/chai": {
|
||||||
|
"version": "4.2.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.21.tgz",
|
||||||
|
"integrity": "sha512-yd+9qKmJxm496BOV9CMNaey8TWsikaZOwMRwPHQIjcOJM9oV+fi9ZMNw3JsVnbEEbo2gRTDnGEBv8pjyn67hNg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/common-tags": {
|
"@types/common-tags": {
|
||||||
"version": "1.8.1",
|
"version": "1.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/common-tags/-/common-tags-1.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/common-tags/-/common-tags-1.8.1.tgz",
|
||||||
|
@ -131,6 +131,7 @@
|
|||||||
"@codesee/tracker": "0.112.1",
|
"@codesee/tracker": "0.112.1",
|
||||||
"@testing-library/jest-dom": "5.14.1",
|
"@testing-library/jest-dom": "5.14.1",
|
||||||
"@testing-library/react": "12.1.2",
|
"@testing-library/react": "12.1.2",
|
||||||
|
"@types/chai": "^4.2.21",
|
||||||
"@types/jest": "26.0.24",
|
"@types/jest": "26.0.24",
|
||||||
"@types/loadable__component": "5.13.4",
|
"@types/loadable__component": "5.13.4",
|
||||||
"@types/lodash-es": "4.17.5",
|
"@types/lodash-es": "4.17.5",
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
// work around for SASS error in Edge
|
|
||||||
// https://github.com/medialize/sass.js/issues/96#issuecomment-424386171
|
|
||||||
if (!self.crypto) {
|
|
||||||
self.crypto = {
|
|
||||||
getRandomValues: function (array) {
|
|
||||||
for (var i = 0, l = array.length; i < l; i++) {
|
|
||||||
array[i] = Math.floor(Math.random() * 256);
|
|
||||||
}
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
self.importScripts('/js/sass.sync.js');
|
|
||||||
|
|
||||||
self.onmessage = e => {
|
|
||||||
const data = e.data;
|
|
||||||
self.Sass.compile(data, result => {
|
|
||||||
if (result.status === 0) {
|
|
||||||
self.postMessage(result.text);
|
|
||||||
} else {
|
|
||||||
self.postMessage({ type: 'error', data: { message: result.formatted } });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
self.postMessage({ type: 'contentLoaded' });
|
|
40
client/src/client/workers/sass-compile.ts
Normal file
40
client/src/client/workers/sass-compile.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/* eslint-disable import/unambiguous */
|
||||||
|
// work around for SASS error in Edge
|
||||||
|
// https://github.com/medialize/sass.js/issues/96#issuecomment-424386171
|
||||||
|
interface WorkerWithSass extends Worker {
|
||||||
|
Sass: {
|
||||||
|
compile(data: unknown, callback: unknown): void;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const ctx: WorkerWithSass & typeof globalThis =
|
||||||
|
self as unknown as WorkerWithSass & typeof globalThis;
|
||||||
|
|
||||||
|
if (!ctx.crypto) {
|
||||||
|
(ctx.crypto as unknown) = {
|
||||||
|
getRandomValues: function (array: number[]) {
|
||||||
|
for (let i = 0, l = array.length; i < l; i++) {
|
||||||
|
array[i] = Math.floor(Math.random() * 256);
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.importScripts('/js/sass.sync.js');
|
||||||
|
|
||||||
|
ctx.onmessage = e => {
|
||||||
|
const data: unknown = e.data;
|
||||||
|
ctx.Sass.compile(data, (result: Record<string, unknown>) => {
|
||||||
|
if (result.status === 0) {
|
||||||
|
ctx.postMessage(result.text);
|
||||||
|
} else {
|
||||||
|
ctx.postMessage({
|
||||||
|
type: 'error',
|
||||||
|
data: { message: result.formatted }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
ctx.postMessage({ type: 'contentLoaded' });
|
@ -6,13 +6,16 @@ import curriculumHelpers, {
|
|||||||
} from '../../utils/curriculum-helpers';
|
} from '../../utils/curriculum-helpers';
|
||||||
import { format as __format } from '../../utils/format';
|
import { format as __format } from '../../utils/format';
|
||||||
|
|
||||||
|
const ctx: Worker & typeof globalThis = self as unknown as Worker &
|
||||||
|
typeof globalThis;
|
||||||
|
|
||||||
const __utils = (() => {
|
const __utils = (() => {
|
||||||
const MAX_LOGS_SIZE = 64 * 1024;
|
const MAX_LOGS_SIZE = 64 * 1024;
|
||||||
|
|
||||||
let logs = [];
|
let logs: string[] = [];
|
||||||
function flushLogs() {
|
function flushLogs() {
|
||||||
if (logs.length) {
|
if (logs.length) {
|
||||||
self.postMessage({
|
ctx.postMessage({
|
||||||
type: 'LOG',
|
type: 'LOG',
|
||||||
data: logs.join('\n')
|
data: logs.join('\n')
|
||||||
});
|
});
|
||||||
@ -20,8 +23,8 @@ const __utils = (() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const oldLog = self.console.log.bind(self.console);
|
const oldLog = ctx.console.log.bind(ctx.console);
|
||||||
function proxyLog(...args) {
|
function proxyLog(...args: string[]) {
|
||||||
logs.push(args.map(arg => __format(arg)).join(' '));
|
logs.push(args.map(arg => __format(arg)).join(' '));
|
||||||
if (logs.join('\n').length > MAX_LOGS_SIZE) {
|
if (logs.join('\n').length > MAX_LOGS_SIZE) {
|
||||||
flushLogs();
|
flushLogs();
|
||||||
@ -30,12 +33,12 @@ const __utils = (() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// unless data.type is truthy, this sends data out to the testRunner
|
// unless data.type is truthy, this sends data out to the testRunner
|
||||||
function postResult(data) {
|
function postResult(data: unknown) {
|
||||||
flushLogs();
|
flushLogs();
|
||||||
self.postMessage(data);
|
ctx.postMessage(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
function log(...msgs) {
|
function log(...msgs: Error[]) {
|
||||||
if (msgs && msgs[0] && !(msgs[0] instanceof chai.AssertionError)) {
|
if (msgs && msgs[0] && !(msgs[0] instanceof chai.AssertionError)) {
|
||||||
// discards the stack trace via toString as it only useful to debug the
|
// discards the stack trace via toString as it only useful to debug the
|
||||||
// site, not a specific challenge.
|
// site, not a specific challenge.
|
||||||
@ -43,8 +46,8 @@ const __utils = (() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const toggleProxyLogger = on => {
|
const toggleProxyLogger = (on: unknown) => {
|
||||||
self.console.log = on ? proxyLog : oldLog;
|
ctx.console.log = on ? proxyLog : oldLog;
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -55,9 +58,25 @@ const __utils = (() => {
|
|||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
interface TestEvaluatorEvent extends MessageEvent {
|
||||||
|
data: {
|
||||||
|
code: {
|
||||||
|
contents: string;
|
||||||
|
editableContents: string;
|
||||||
|
};
|
||||||
|
removeComments: boolean;
|
||||||
|
firstTest: unknown;
|
||||||
|
testString: string;
|
||||||
|
build: string;
|
||||||
|
sources: {
|
||||||
|
[fileName: string]: unknown;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/* Run the test if there is one. If not just evaluate the user code */
|
/* Run the test if there is one. If not just evaluate the user code */
|
||||||
self.onmessage = async e => {
|
ctx.onmessage = async (e: TestEvaluatorEvent) => {
|
||||||
/* eslint-disable no-unused-vars */
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
let code = (e.data?.code?.contents || '').slice();
|
let code = (e.data?.code?.contents || '').slice();
|
||||||
code = e.data?.removeComments ? removeJSComments(code) : code;
|
code = e.data?.removeComments ? removeJSComments(code) : code;
|
||||||
let editableContents = (e.data?.code?.editableContents || '').slice();
|
let editableContents = (e.data?.code?.editableContents || '').slice();
|
||||||
@ -68,13 +87,16 @@ self.onmessage = async e => {
|
|||||||
const assert = chai.assert;
|
const assert = chai.assert;
|
||||||
const __helpers = curriculumHelpers;
|
const __helpers = curriculumHelpers;
|
||||||
// Fake Deep Equal dependency
|
// Fake Deep Equal dependency
|
||||||
const DeepEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
const DeepEqual = (a: unknown, b: unknown) =>
|
||||||
|
JSON.stringify(a) === JSON.stringify(b);
|
||||||
|
|
||||||
// Build errors should be reported, but only once:
|
// Build errors should be reported, but only once:
|
||||||
__utils.toggleProxyLogger(e.data.firstTest);
|
__utils.toggleProxyLogger(e.data.firstTest);
|
||||||
/* eslint-enable no-unused-vars */
|
/* eslint-enable @typescript-eslint/no-unused-vars */
|
||||||
try {
|
try {
|
||||||
let testResult;
|
let testResult;
|
||||||
|
// This can be reassigned by the eval inside the try block, so it should be declared as a let
|
||||||
|
// eslint-disable-next-line prefer-const
|
||||||
let __userCodeWasExecuted = false;
|
let __userCodeWasExecuted = false;
|
||||||
/* eslint-disable no-eval */
|
/* eslint-disable no-eval */
|
||||||
try {
|
try {
|
||||||
@ -86,7 +108,7 @@ self.onmessage = async e => {
|
|||||||
__utils.flushLogs();
|
__utils.flushLogs();
|
||||||
__userCodeWasExecuted = true;
|
__userCodeWasExecuted = true;
|
||||||
__utils.toggleProxyLogger(true);
|
__utils.toggleProxyLogger(true);
|
||||||
${e.data.testString}`);
|
${e.data.testString}`) as unknown;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (__userCodeWasExecuted) {
|
if (__userCodeWasExecuted) {
|
||||||
// rethrow error, since test failed.
|
// rethrow error, since test failed.
|
||||||
@ -95,19 +117,21 @@ ${e.data.testString}`);
|
|||||||
// log build errors unless they're related to import/export/require (there
|
// log build errors unless they're related to import/export/require (there
|
||||||
// are challenges that use them and they should not trigger warnings)
|
// are challenges that use them and they should not trigger warnings)
|
||||||
if (
|
if (
|
||||||
err.name !== 'ReferenceError' ||
|
(err as Error).name !== 'ReferenceError' ||
|
||||||
(err.message !== 'require is not defined' &&
|
((err as Error).message !== 'require is not defined' &&
|
||||||
err.message !== 'exports is not defined')
|
(err as Error).message !== 'exports is not defined')
|
||||||
) {
|
) {
|
||||||
__utils.log(err);
|
__utils.log(err as Error);
|
||||||
}
|
}
|
||||||
// the tests may not require working code, so they are evaluated even if
|
// the tests may not require working code, so they are evaluated even if
|
||||||
// the user code does not get executed.
|
// the user code does not get executed.
|
||||||
testResult = eval(e.data.testString);
|
testResult = eval(e.data.testString) as unknown;
|
||||||
}
|
}
|
||||||
/* eslint-enable no-eval */
|
/* eslint-enable no-eval */
|
||||||
if (typeof testResult === 'function') {
|
if (typeof testResult === 'function') {
|
||||||
await testResult(fileName => __toString(e.data.sources[fileName]));
|
await testResult((fileName: string) =>
|
||||||
|
__toString(e.data.sources[fileName])
|
||||||
|
);
|
||||||
}
|
}
|
||||||
__utils.postResult({
|
__utils.postResult({
|
||||||
pass: true
|
pass: true
|
||||||
@ -117,15 +141,15 @@ ${e.data.testString}`);
|
|||||||
__utils.toggleProxyLogger(false);
|
__utils.toggleProxyLogger(false);
|
||||||
// Report execution errors in case user code has errors that are only
|
// Report execution errors in case user code has errors that are only
|
||||||
// uncovered during testing.
|
// uncovered during testing.
|
||||||
__utils.log(err);
|
__utils.log(err as Error);
|
||||||
// postResult flushes the logs and must be called after logging is finished.
|
// postResult flushes the logs and must be called after logging is finished.
|
||||||
__utils.postResult({
|
__utils.postResult({
|
||||||
err: {
|
err: {
|
||||||
message: err.message,
|
message: (err as Error).message,
|
||||||
stack: err.stack
|
stack: (err as Error).stack
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.postMessage({ type: 'contentLoaded' });
|
ctx.postMessage({ type: 'contentLoaded' });
|
@ -1,6 +1,11 @@
|
|||||||
{
|
{
|
||||||
"include": ["./i18n/**/*", "./plugins/**/*", "./src/**/*", "./utils/**/*"],
|
"include": ["./i18n/**/*", "./plugins/**/*", "./src/**/*", "./utils/**/*"],
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
|
"lib": [
|
||||||
|
"WebWorker",
|
||||||
|
"DOM",
|
||||||
|
"DOM.Iterable"
|
||||||
|
],
|
||||||
"target": "es2020",
|
"target": "es2020",
|
||||||
"module": "es2020",
|
"module": "es2020",
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
|
@ -12,8 +12,8 @@ module.exports = (env = {}) => {
|
|||||||
mode: __DEV__ ? 'development' : 'production',
|
mode: __DEV__ ? 'development' : 'production',
|
||||||
entry: {
|
entry: {
|
||||||
'frame-runner': './src/client/frame-runner.js',
|
'frame-runner': './src/client/frame-runner.js',
|
||||||
'sass-compile': './src/client/workers/sass-compile.js',
|
'sass-compile': './src/client/workers/sass-compile.ts',
|
||||||
'test-evaluator': './src/client/workers/test-evaluator.js'
|
'test-evaluator': './src/client/workers/test-evaluator.ts'
|
||||||
},
|
},
|
||||||
devtool: __DEV__ ? 'inline-source-map' : 'source-map',
|
devtool: __DEV__ ? 'inline-source-map' : 'source-map',
|
||||||
output: {
|
output: {
|
||||||
|
Reference in New Issue
Block a user