Skip to content

Commit fa16122

Browse files
committed
Support strict mode
1 parent 9be36ff commit fa16122

7 files changed

+76
-47
lines changed

js/main.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -72,5 +72,7 @@ export default function denoMain() {
7272

7373
const inputFn = argv[1];
7474
const mod = runtime.resolveModule(inputFn, `${cwd}/`);
75-
mod.compileAndRun();
75+
assert(mod != null);
76+
// TypeScript does not track assert, therefore not null assertion
77+
mod!.compileAndRun();
7678
}

js/os.ts

+16-7
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,14 @@ export function codeFetch(
3737
fbs.Base.addMsgType(builder, fbs.Any.CodeFetch);
3838
builder.finish(fbs.Base.endBase(builder));
3939
const resBuf = libdeno.send(builder.asUint8Array());
40+
assert(resBuf != null);
4041
// Process CodeFetchRes
41-
const bb = new flatbuffers.ByteBuffer(new Uint8Array(resBuf));
42+
// TypeScript does not track `assert` from a CFA perspective, therefore not
43+
// null assertion `!`
44+
const bb = new flatbuffers.ByteBuffer(new Uint8Array(resBuf!));
4245
const baseRes = fbs.Base.getRootAsBase(bb);
4346
if (fbs.Any.NONE === baseRes.msgType()) {
44-
throw Error(baseRes.error());
47+
throw Error(baseRes.error()!);
4548
}
4649
assert(fbs.Any.CodeFetchRes === baseRes.msgType());
4750
const codeFetchRes = new fbs.CodeFetchRes();
@@ -80,7 +83,9 @@ export function codeCache(
8083
const bb = new flatbuffers.ByteBuffer(new Uint8Array(resBuf));
8184
const baseRes = fbs.Base.getRootAsBase(bb);
8285
assert(fbs.Any.NONE === baseRes.msgType());
83-
throw Error(baseRes.error());
86+
// undefined and null are incompatible in strict mode, but at runtime
87+
// a null value is fine, therefore not null assertion
88+
throw Error(baseRes.error()!);
8489
}
8590
}
8691

@@ -103,16 +108,20 @@ export function readFileSync(filename: string): Uint8Array {
103108
builder.finish(fbs.Base.endBase(builder));
104109
const resBuf = libdeno.send(builder.asUint8Array());
105110
assert(resBuf != null);
106-
107-
const bb = new flatbuffers.ByteBuffer(new Uint8Array(resBuf));
111+
// TypeScript does not track `assert` from a CFA perspective, therefore not
112+
// null assertion `!`
113+
const bb = new flatbuffers.ByteBuffer(new Uint8Array(resBuf!));
108114
const baseRes = fbs.Base.getRootAsBase(bb);
109115
if (fbs.Any.NONE === baseRes.msgType()) {
110-
throw Error(baseRes.error());
116+
// undefined and null are incompatible in strict mode, but at runtime
117+
// a null value is fine, therefore not null assertion
118+
throw Error(baseRes.error()!);
111119
}
112120
assert(fbs.Any.ReadFileSyncRes === baseRes.msgType());
113121
const res = new fbs.ReadFileSyncRes();
114122
assert(baseRes.msg(res) != null);
115-
return new Uint8Array(res.dataArray());
123+
// TypeScript cannot track assertion above, therefore not null assertion
124+
return new Uint8Array(res.dataArray()!);
116125
}
117126

118127
export function writeFileSync(

js/runtime.ts

+36-24
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export function setup(): void {
5757
const mod = FileModule.load(filename);
5858
if (!mod) {
5959
util.log("getGeneratedContents cannot find", filename);
60-
return null;
60+
return "";
6161
}
6262
return mod.outputCode;
6363
}
@@ -71,7 +71,7 @@ export function setup(): void {
7171
// FileModule.load(). FileModules are NOT executed upon first load, only when
7272
// compileAndRun is called.
7373
export class FileModule {
74-
scriptVersion: string;
74+
scriptVersion = "";
7575
readonly exports = {};
7676

7777
private static readonly map = new Map<string, FileModule>();
@@ -105,15 +105,15 @@ export class FileModule {
105105
execute(this.fileName, this.outputCode);
106106
}
107107

108-
static load(fileName: string): FileModule {
108+
static load(fileName: string): FileModule | undefined {
109109
return this.map.get(fileName);
110110
}
111111

112112
static getScriptsWithSourceCode(): string[] {
113113
const out = [];
114114
for (const fn of this.map.keys()) {
115115
const m = this.map.get(fn);
116-
if (m.sourceCode) {
116+
if (m && m.sourceCode) {
117117
out.push(fn);
118118
}
119119
}
@@ -127,9 +127,10 @@ export function makeDefine(fileName: string): AmdDefine {
127127
log("localRequire", x);
128128
};
129129
const currentModule = FileModule.load(fileName);
130-
const localExports = currentModule.exports;
130+
util.assert(currentModule != null);
131+
const localExports = currentModule!.exports;
131132
log("localDefine", fileName, deps, localExports);
132-
const args = deps.map(dep => {
133+
const args = deps.map((dep) => {
133134
if (dep === "require") {
134135
return localRequire;
135136
} else if (dep === "exports") {
@@ -140,9 +141,13 @@ export function makeDefine(fileName: string): AmdDefine {
140141
return deno;
141142
} else {
142143
const resolved = resolveModuleName(dep, fileName);
143-
const depModule = FileModule.load(resolved);
144-
depModule.compileAndRun();
145-
return depModule.exports;
144+
util.assert(resolved != null);
145+
const depModule = FileModule.load(resolved!);
146+
if (depModule) {
147+
depModule.compileAndRun();
148+
return depModule.exports;
149+
}
150+
return undefined;
146151
}
147152
});
148153
factory(...args);
@@ -156,10 +161,14 @@ export function resolveModule(
156161
): null | FileModule {
157162
util.log("resolveModule", { moduleSpecifier, containingFile });
158163
util.assert(moduleSpecifier != null && moduleSpecifier.length > 0);
159-
let filename: string, sourceCode: string, outputCode: string;
164+
let filename: string | null;
165+
let sourceCode: string | null;
166+
let outputCode: string | null;
160167
if (moduleSpecifier.startsWith(ASSETS) || containingFile.startsWith(ASSETS)) {
161168
// Assets are compiled into the runtime javascript bundle.
162-
const moduleId = moduleSpecifier.split("/").pop();
169+
// we _know_ `.pop()` will return a string, but TypeScript doesn't so
170+
// not null assertion
171+
const moduleId = moduleSpecifier.split("/").pop()!;
163172
const assetName = moduleId.includes(".") ? moduleId : `${moduleId}.d.ts`;
164173
util.assert(assetName in assetSourceCode, `No such asset "${assetName}"`);
165174
sourceCode = assetSourceCode[assetName];
@@ -179,15 +188,17 @@ export function resolveModule(
179188
sourceCode = fetchResponse.sourceCode;
180189
outputCode = fetchResponse.outputCode;
181190
}
182-
if (sourceCode == null || sourceCode.length === 0) {
191+
if (sourceCode == null || sourceCode.length === 0 || filename == null) {
183192
return null;
184193
}
185194
util.log("resolveModule sourceCode length ", sourceCode.length);
186195
const m = FileModule.load(filename);
187196
if (m != null) {
188197
return m;
189198
} else {
190-
return new FileModule(filename, sourceCode, outputCode);
199+
// null and undefined are incompatible in strict mode, but outputCode being
200+
// null here has no runtime behavior impact, therefore not null assertion
201+
return new FileModule(filename, sourceCode, outputCode!);
191202
}
192203
}
193204

@@ -204,7 +215,7 @@ function resolveModuleName(
204215
}
205216

206217
function execute(fileName: string, outputCode: string): void {
207-
util.assert(outputCode && outputCode.length > 0);
218+
util.assert(outputCode != null && outputCode.length > 0);
208219
window["define"] = makeDefine(fileName);
209220
outputCode += `\n//# sourceURL=${fileName}`;
210221
globalEval(outputCode);
@@ -278,7 +289,7 @@ class TypeScriptHost implements ts.LanguageServiceHost {
278289
getScriptVersion(fileName: string): string {
279290
util.log("getScriptVersion", fileName);
280291
const m = FileModule.load(fileName);
281-
return m.scriptVersion;
292+
return m && m.scriptVersion || "";
282293
}
283294

284295
getScriptSnapshot(fileName: string): ts.IScriptSnapshot | undefined {
@@ -323,32 +334,33 @@ class TypeScriptHost implements ts.LanguageServiceHost {
323334
const fn = "lib.globals.d.ts"; // ts.getDefaultLibFileName(options);
324335
util.log("getDefaultLibFileName", fn);
325336
const m = resolveModule(fn, ASSETS);
326-
return m.fileName;
337+
util.assert(m != null);
338+
// TypeScript cannot track assertions, therefore not null assertion
339+
return m!.fileName;
327340
}
328341

329342
resolveModuleNames(
330343
moduleNames: string[],
331-
containingFile: string,
332-
reusedNames?: string[]
333-
): Array<ts.ResolvedModule | undefined> {
344+
containingFile: string
345+
): ts.ResolvedModule[] {
334346
//util.log("resolveModuleNames", { moduleNames, reusedNames });
335-
return moduleNames.map((name: string) => {
347+
return moduleNames.map((name) => {
336348
let resolvedFileName;
337349
if (name === "deno") {
338350
resolvedFileName = resolveModuleName("deno.d.ts", ASSETS);
339351
} else if (name === "typescript") {
340352
resolvedFileName = resolveModuleName("typescript.d.ts", ASSETS);
341353
} else {
342354
resolvedFileName = resolveModuleName(name, containingFile);
343-
if (resolvedFileName == null) {
344-
return undefined;
345-
}
355+
}
356+
if (resolvedFileName == null) {
357+
return undefined;
346358
}
347359
// This flags to the compiler to not go looking to transpile functional
348360
// code, anything that is in `/$asset$/` is just library code
349361
const isExternalLibraryImport = resolvedFileName.startsWith(ASSETS);
350362
return { resolvedFileName, isExternalLibraryImport };
351-
});
363+
}).filter((mod) => mod != null) as ts.ResolvedModule[];
352364
}
353365
}
354366

js/types.d.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
export type TypedArray = Uint8Array | Float32Array | Int32Array;
33

44
export interface ModuleInfo {
5-
moduleName?: string;
6-
filename?: string;
7-
sourceCode?: string;
8-
outputCode?: string;
5+
moduleName: string | null;
6+
filename: string | null;
7+
sourceCode: string | null;
8+
outputCode: string | null;
99
}
1010

1111
// Following definitions adapted from:

js/util.ts

+8-3
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,20 @@ export function arrayToStr(ui8: Uint8Array): string {
4949
// produces broken code when targeting ES5 code.
5050
// See https://github.com/Microsoft/TypeScript/issues/15202
5151
// At the time of writing, the github issue is closed but the problem remains.
52-
export interface Resolvable<T> extends Promise<T> {
52+
export interface ResolvableMethods<T> {
5353
resolve: (value?: T | PromiseLike<T>) => void;
5454
// tslint:disable-next-line:no-any
5555
reject: (reason?: any) => void;
5656
}
57+
58+
type Resolvable<T> = Promise<T> & ResolvableMethods<T>;
59+
5760
export function createResolvable<T>(): Resolvable<T> {
58-
let methods;
61+
let methods: ResolvableMethods<T>;
5962
const promise = new Promise<T>((resolve, reject) => {
6063
methods = { resolve, reject };
6164
});
62-
return Object.assign(promise, methods) as Resolvable<T>;
65+
// TypeScript doesn't know that the Promise callback occurs synchronously
66+
// therefore use of not null assertion (`!`)
67+
return Object.assign(promise, methods!) as Resolvable<T>;
6368
}

js/v8_source_maps.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export function prepareStackTraceWrapper(
4040
try {
4141
return prepareStackTrace(error, stack);
4242
} catch (prepareStackError) {
43-
Error.prepareStackTrace = null;
43+
Error.prepareStackTrace = undefined;
4444
console.log("=====Error inside of prepareStackTrace====");
4545
console.log(prepareStackError.stack.toString());
4646
console.log("=====Original error=======================");
@@ -66,8 +66,8 @@ export function wrapCallSite(frame: CallSite): CallSite {
6666
const source = frame.getFileName() || frame.getScriptNameOrSourceURL();
6767

6868
if (source) {
69-
const line = frame.getLineNumber();
70-
const column = frame.getColumnNumber() - 1;
69+
const line = frame.getLineNumber() || 0;
70+
const column = (frame.getColumnNumber() || 1) - 1;
7171
const position = mapSourcePosition({ source, line, column });
7272
frame = cloneCallSite(frame);
7373
frame.getFileName = () => position.source;
@@ -79,7 +79,7 @@ export function wrapCallSite(frame: CallSite): CallSite {
7979
}
8080

8181
// Code called using eval() needs special handling
82-
let origin = frame.isEval() && frame.getEvalOrigin();
82+
let origin = frame.isEval() && frame.getEvalOrigin() || undefined;
8383
if (origin) {
8484
origin = mapEvalOrigin(origin);
8585
frame = cloneCallSite(frame);
@@ -115,7 +115,7 @@ function CallSiteToString(frame: CallSite): string {
115115
} else {
116116
fileName = frame.getScriptNameOrSourceURL();
117117
if (!fileName && frame.isEval()) {
118-
fileLocation = frame.getEvalOrigin();
118+
fileLocation = frame.getEvalOrigin() || "";
119119
fileLocation += ", "; // Expecting source position to follow.
120120
}
121121

@@ -181,7 +181,7 @@ function CallSiteToString(frame: CallSite): string {
181181
// Regex for detecting source maps
182182
const reSourceMap = /^data:application\/json[^,]+base64,/;
183183

184-
function loadConsumer(source: string): SourceMapConsumer {
184+
function loadConsumer(source: string): SourceMapConsumer | null {
185185
let consumer = consumers.get(source);
186186
if (consumer == null) {
187187
const code = getGeneratedContents(source);
@@ -221,7 +221,7 @@ function loadConsumer(source: string): SourceMapConsumer {
221221
return consumer;
222222
}
223223

224-
function retrieveSourceMapURL(fileData: string): string {
224+
function retrieveSourceMapURL(fileData: string): string | null {
225225
// Get the URL of the source map
226226
// tslint:disable-next-line:max-line-length
227227
const re = /(?:\/\/[@#][ \t]+sourceMappingURL=([^\s'"]+?)[ \t]*$)|(?:\/\*[@#][ \t]+sourceMappingURL=([^\*]+?)[ \t]*(?:\*\/)[ \t]*$)/gm;

tsconfig.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,18 @@
44
"baseUrl": ".",
55
"module": "esnext",
66
"moduleResolution": "node",
7-
"noImplicitAny": true,
87
"noImplicitReturns": true,
98
"noFallthroughCasesInSwitch": true,
109
"noLib": true,
10+
"noUnusedLocals": true,
1111
"paths": {
1212
"*": ["*", "out/debug/*", "out/default/*", "out/release/*"]
1313
},
1414
"preserveConstEnums": true,
1515
"pretty": true,
1616
"removeComments": true,
1717
"sourceMap": true,
18+
"strict": true,
1819
"target": "esnext",
1920
"types": []
2021
},

0 commit comments

Comments
 (0)