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:
Meron Ogbai
2021-10-14 21:44:55 +03:00
committed by GitHub
parent 9bb1919e83
commit 06518c04a7
7 changed files with 102 additions and 53 deletions

View File

@ -5013,6 +5013,12 @@
"@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": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/@types/common-tags/-/common-tags-1.8.1.tgz",

View File

@ -131,6 +131,7 @@
"@codesee/tracker": "0.112.1",
"@testing-library/jest-dom": "5.14.1",
"@testing-library/react": "12.1.2",
"@types/chai": "^4.2.21",
"@types/jest": "26.0.24",
"@types/loadable__component": "5.13.4",
"@types/lodash-es": "4.17.5",

View File

@ -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' });

View 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' });

View File

@ -6,13 +6,16 @@ import curriculumHelpers, {
} from '../../utils/curriculum-helpers';
import { format as __format } from '../../utils/format';
const ctx: Worker & typeof globalThis = self as unknown as Worker &
typeof globalThis;
const __utils = (() => {
const MAX_LOGS_SIZE = 64 * 1024;
let logs = [];
let logs: string[] = [];
function flushLogs() {
if (logs.length) {
self.postMessage({
ctx.postMessage({
type: 'LOG',
data: logs.join('\n')
});
@ -20,8 +23,8 @@ const __utils = (() => {
}
}
const oldLog = self.console.log.bind(self.console);
function proxyLog(...args) {
const oldLog = ctx.console.log.bind(ctx.console);
function proxyLog(...args: string[]) {
logs.push(args.map(arg => __format(arg)).join(' '));
if (logs.join('\n').length > MAX_LOGS_SIZE) {
flushLogs();
@ -30,12 +33,12 @@ const __utils = (() => {
}
// unless data.type is truthy, this sends data out to the testRunner
function postResult(data) {
function postResult(data: unknown) {
flushLogs();
self.postMessage(data);
ctx.postMessage(data);
}
function log(...msgs) {
function log(...msgs: Error[]) {
if (msgs && msgs[0] && !(msgs[0] instanceof chai.AssertionError)) {
// discards the stack trace via toString as it only useful to debug the
// site, not a specific challenge.
@ -43,8 +46,8 @@ const __utils = (() => {
}
}
const toggleProxyLogger = on => {
self.console.log = on ? proxyLog : oldLog;
const toggleProxyLogger = (on: unknown) => {
ctx.console.log = on ? proxyLog : oldLog;
};
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 */
self.onmessage = async e => {
/* eslint-disable no-unused-vars */
ctx.onmessage = async (e: TestEvaluatorEvent) => {
/* eslint-disable @typescript-eslint/no-unused-vars */
let code = (e.data?.code?.contents || '').slice();
code = e.data?.removeComments ? removeJSComments(code) : code;
let editableContents = (e.data?.code?.editableContents || '').slice();
@ -68,13 +87,16 @@ self.onmessage = async e => {
const assert = chai.assert;
const __helpers = curriculumHelpers;
// 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:
__utils.toggleProxyLogger(e.data.firstTest);
/* eslint-enable no-unused-vars */
/* eslint-enable @typescript-eslint/no-unused-vars */
try {
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;
/* eslint-disable no-eval */
try {
@ -86,7 +108,7 @@ self.onmessage = async e => {
__utils.flushLogs();
__userCodeWasExecuted = true;
__utils.toggleProxyLogger(true);
${e.data.testString}`);
${e.data.testString}`) as unknown;
} catch (err) {
if (__userCodeWasExecuted) {
// rethrow error, since test failed.
@ -95,19 +117,21 @@ ${e.data.testString}`);
// log build errors unless they're related to import/export/require (there
// are challenges that use them and they should not trigger warnings)
if (
err.name !== 'ReferenceError' ||
(err.message !== 'require is not defined' &&
err.message !== 'exports is not defined')
(err as Error).name !== 'ReferenceError' ||
((err as Error).message !== 'require 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 user code does not get executed.
testResult = eval(e.data.testString);
testResult = eval(e.data.testString) as unknown;
}
/* eslint-enable no-eval */
if (typeof testResult === 'function') {
await testResult(fileName => __toString(e.data.sources[fileName]));
await testResult((fileName: string) =>
__toString(e.data.sources[fileName])
);
}
__utils.postResult({
pass: true
@ -117,15 +141,15 @@ ${e.data.testString}`);
__utils.toggleProxyLogger(false);
// Report execution errors in case user code has errors that are only
// uncovered during testing.
__utils.log(err);
__utils.log(err as Error);
// postResult flushes the logs and must be called after logging is finished.
__utils.postResult({
err: {
message: err.message,
stack: err.stack
message: (err as Error).message,
stack: (err as Error).stack
}
});
}
};
self.postMessage({ type: 'contentLoaded' });
ctx.postMessage({ type: 'contentLoaded' });

View File

@ -1,6 +1,11 @@
{
"include": ["./i18n/**/*", "./plugins/**/*", "./src/**/*", "./utils/**/*"],
"compilerOptions": {
"lib": [
"WebWorker",
"DOM",
"DOM.Iterable"
],
"target": "es2020",
"module": "es2020",
"moduleResolution": "node",

View File

@ -12,8 +12,8 @@ module.exports = (env = {}) => {
mode: __DEV__ ? 'development' : 'production',
entry: {
'frame-runner': './src/client/frame-runner.js',
'sass-compile': './src/client/workers/sass-compile.js',
'test-evaluator': './src/client/workers/test-evaluator.js'
'sass-compile': './src/client/workers/sass-compile.ts',
'test-evaluator': './src/client/workers/test-evaluator.ts'
},
devtool: __DEV__ ? 'inline-source-map' : 'source-map',
output: {