Skip to content

Commit 90ab53f

Browse files
committed
type & lint checks for mcconfig & mcrun
1 parent c4de4d4 commit 90ab53f

File tree

7 files changed

+143
-43
lines changed

7 files changed

+143
-43
lines changed

build/devices/zephyr/config/CMakeLists.txt.prefix

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,7 @@ set(TYPESCRIPT_SOURCE_FILES
8282
set(TYPESCRIPT_CONVERTED_FILES
8383
)
8484

85+
set(TYPECHECK_FILE ${CMAKE_BINARY_DIR}/.typeCheck)
86+
87+
8588
# --- end prefix ---

build/devices/zephyr/config/CMakeLists.txt.suffix

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,4 +99,15 @@ if (TYPESCRIPT_CONVERTED_FILES)
9999
)
100100
endif()
101101

102+
if (JAVASCRIPT_SOURCE_FILES)
103+
add_custom_command(
104+
OUTPUT ${TYPECHECK_FILE}
105+
COMMAND tsc -p ${MODULES_DIR}/tsconfig-js.json
106+
DEPENDS ${MODULES_DIR}/tsconfig-js.json ${TMP_DIR}/mc.devicetree.d.ts ${JAVASCRIPT_SOURCE_FILES}
107+
COMMAND ${CMAKE_COMMAND} -E touch ${TYPECHECK_FILE}
108+
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
109+
VERBATIM
110+
)
111+
endif()
112+
102113
# --- end suffix

examples/manifest_base.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
"main": "main"
2020
},
2121
"include": [
22-
"$(MODULES)/files/resource/manifest.json"
22+
"$(MODULES)/files/resource/manifest.json",
23+
"$(MODDABLE)/examples/manifest_typings.json"
2324
],
2425
"modules": {
2526
"*": [

tools/mcconfig.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2016-2025 Moddable Tech, Inc.
2+
* Copyright (c) 2016-2026 Moddable Tech, Inc.
33
*
44
* This file is part of the Moddable SDK Tools.
55
*
@@ -1421,7 +1421,12 @@ export default class extends Tool {
14211421

14221422
if (this.tsFiles.length) {
14231423
file = new TSConfigFile(this.modulesPath + this.slash + "tsconfig.json");
1424-
file.generate(this);
1424+
file.generate(this, true, false);
1425+
}
1426+
1427+
if (this.jsFiles.length) {
1428+
file = new TSConfigFile(this.modulesPath + this.slash + "tsconfig-js.json");
1429+
file.generate(this, false, true);
14251430
}
14261431

14271432
if (this.make) {

tools/mcdevicetree.js

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2025 Moddable Tech, Inc.
2+
* Copyright (c) 2025-2026 Moddable Tech, Inc.
33
*
44
* This file is part of the Moddable SDK Tools.
55
*
@@ -176,19 +176,6 @@ const device = {
176176
pin: {}
177177
};
178178
`
179-
state.tsCode += `
180-
declare module "embedded:provider/builtin" {
181-
interface DeviceIO {}
182-
interface DeviceNetwork {}
183-
184-
interface Device {
185-
io: DeviceIO;
186-
network: DeviceNetwork;
187-
}
188-
}
189-
190-
`;
191-
192179
doAliases(state, parsed);
193180
doGPIOBanks(state, parsed);
194181
doGPIOs(state, parsed);
@@ -346,7 +333,18 @@ device.spi = {};
346333
export default device;
347334
`;
348335

349-
state.tsCode += `
336+
if (state.tsDeviceIO || state.tsDeviceNetwork) {
337+
state.tsCode += `declare module "embedded:provider/builtin" {\n`;
338+
state.tsCode += `\tinterface Device {\n`;
339+
if (state.tsDeviceIO)
340+
state.tsCode += `\t\tio: DeviceIO;\n`;
341+
if (state.tsDeviceNetwork)
342+
state.tsCode += `\t\tnetwork: DeviceNetwork;\n`;
343+
state.tsCode += `\t}\n`;
344+
state.tsCode += `}\n`;
345+
}
346+
347+
state.tsCode += `
350348
declare module "embedded:provider/builtin" {
351349
const device: Device
352350
export default device;
@@ -518,6 +516,7 @@ declare module "embedded:provider/builtin" {
518516
DigitalBank: typeof DigitalBank;
519517
}
520518
`;
519+
state.tsDeviceIO = true;
521520

522521
const kinds = new Set, leds = new Set, buttons = new Set;
523522
gpios.forEach(gpio => {
@@ -582,7 +581,7 @@ device.${kindName}.${gpio.name} = class {${gpio.userName ? " // " + gpio.userNam
582581

583582
if (leds.size) {
584583
state.tsCode += `
585-
interface DeviceLEDOptions extends Omit<ConstructorParameters<typeof Digital>[0], 'pin' | 'mode' | 'port'> {}
584+
type DeviceLEDOptions = Omit<ConstructorParameters<typeof Digital>[0], 'pin' | 'mode' | 'port'>;
586585
interface DeviceLEDs {
587586
`;
588587
leds.forEach(value => {
@@ -723,6 +722,7 @@ const struct modZephyr${options.name} *modZephyrGet${options.name}(const char *l
723722
state.tsCode += `\t\t${options.moduleDefault}: typeof ${options.moduleDefault}\n`;
724723
state.tsCode += "\t}\n";
725724
state.tsCode += "\n";
725+
state.tsDeviceIO = true;
726726
}
727727
state.tsCode += `\ttype ${options.moduleDefault}Options = ConstructorParameters<typeof ${options.moduleDefault}>[0] & {\n`;
728728
state.tsCode += `\t\tio: typeof ${options.moduleDefault}\n`;
@@ -922,6 +922,7 @@ declare module "embedded:provider/builtin" {
922922
}
923923
`;
924924
});
925+
state.tsDeviceNetwork = true;
925926
}
926927

927928

tools/mcmanifest.js

Lines changed: 95 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2016-2025 Moddable Tech, Inc.
2+
* Copyright (c) 2016-2026 Moddable Tech, Inc.
33
*
44
* This file is part of the Moddable SDK Tools.
55
*
@@ -725,26 +725,52 @@ otadata, data, ota, , ${OTADATA_SIZE},`;
725725
options += " -d";
726726
if (tool.nativeCode)
727727
options += " -c";
728+
const check = (TSConfigFile.filter(tool, [{source}])).length;
729+
const typeCheck = tool.typeCheck && check;
730+
const lintCheck = tool.lintCheck && check;
728731

729732
if (tool.platform == "zephyr") {
730733
source = source.replaceAll("#", tool.escapedHash);
731734
var output = "${MODULES_DIR}" + tool.slash + target.replaceAll("#", tool.escapedHash);
732735
var outputPath = output.slice(0, output.lastIndexOf("/"));
733736
this.line("add_custom_command(");
734737
this.line("\tOUTPUT " + output);
738+
if (lintCheck)
739+
this.line("\tCOMMAND eslint " + source + " --config ${MODDABLE}/eslint.config.mjs");
735740
this.line("\tCOMMAND xsc " + source + " " + options + " -e -o " + outputPath + " -r " + targetParts.name.replaceAll("#", tool.escapedHash));
736-
this.line("\tDEPENDS " + source);
741+
this.line("\tDEPENDS " + source + (typeCheck ? " ${TYPECHECK_FILE}" : ""));
742+
this.line("\tWORKING_DIRECTORY ${MODDABLE}");
737743
this.line("\tVERBATIM)");
738744
this.line("");
739745
}
740746
else {
741-
this.line("$(MODULES_DIR)", tool.slash, target.replaceAll("#", tool.escapedHash), ": ", source.replaceAll("#", tool.escapedHash));
747+
this.line("$(MODULES_DIR)", tool.slash, target.replaceAll("#", tool.escapedHash), ": ", source.replaceAll("#", tool.escapedHash), typeCheck ? " $(MODULES_DIR)" + tool.slash + ".typeCheck" : "");
748+
if (lintCheck) {
749+
this.echo(tool, "eslint ", source.split(tool.slash).at(-1));
750+
this.line("\tcd $(MODDABLE) && eslint ", source, " --config $(MODDABLE)/eslint.config.mjs");
751+
}
742752
this.echo(tool, "xsc ", target);
743753
this.line("\txsc ", source, options, " -e -o $(@D) -r ", targetParts.name.replaceAll("#", "\\#"));
744754
}
745755
}
746756
this.line("");
747757

758+
if (tool.typeCheck) {
759+
const sources = TSConfigFile.filter(tool, tool.jsFiles);
760+
if ("zephyr" === tool.platform) {
761+
this.line("set(JAVASCRIPT_SOURCE_FILES");
762+
sources.map(file => file.source.replaceAll("#", tool.escapedHash)).forEach(source => this.line("\t", source));
763+
this.line(")");
764+
}
765+
else {
766+
this.line("$(MODULES_DIR)", tool.slash, ".typeCheck: " + sources.map(item => item.source).join(" "));
767+
this.echo(tool, "tsc ", "tsconfig-js.json", " (typeCheck JavaScript)");
768+
this.line("\t", tool.typescript.compiler, " -p $(MODULES_DIR)", tool.slash, "tsconfig-js.json");
769+
this.line("\t", "touch $(MODULES_DIR)", tool.slash, ".typeCheck");
770+
this.line("");
771+
}
772+
}
773+
748774
if (tool.tsFiles.length) {
749775
let directories = tool.tsFiles.map(item => tool.splitPath(item.source).directory);
750776
const length = directories.length;
@@ -796,8 +822,11 @@ otadata, data, ota, , ${OTADATA_SIZE},`;
796822

797823
this.line("add_custom_command(");
798824
this.line("\tOUTPUT ${MODULES_DIR}", temporary.slice(0,-3), ".xsb");
825+
if (tool.lintCheck)
826+
this.line("\tCOMMAND eslint " + source + " --config ${MODDABLE}/eslint.config.mjs");
799827
this.line("\tCOMMAND xsc ${MODULES_DIR}", temporary, options, " -e -o ${MODULES_DIR} -r ", targetParts.name.replaceAll("#", tool.escapedHash));
800828
this.line("\tDEPENDS ${MODULES_DIR}", temporary);
829+
this.line("\tWORKING_DIRECTORY ${MODDABLE}");
801830
this.line("\tVERBATIM");
802831
this.line(")");
803832
this.line("");
@@ -810,6 +839,11 @@ otadata, data, ota, , ${OTADATA_SIZE},`;
810839
var targetParts = tool.splitPath(target);
811840
var temporary = source.slice(common, -3) + ".js"
812841
this.line("$(MODULES_DIR)", tool.slash, target.replaceAll("#", tool.escapedHash), ": $(MODULES_DIR)", temporary.replaceAll("#", tool.escapedHash));
842+
843+
if (tool.lintCheck) {
844+
this.echo(tool, "eslint ", source.split(tool.slash).at(-1));
845+
this.line("\tcd $(MODDABLE) && eslint ", source, " --config $(MODDABLE)/eslint.config.mjs");
846+
}
813847
this.echo(tool, "xsc ", target);
814848
var options = "";
815849
if (result.commonjs)
@@ -1637,11 +1671,8 @@ trace(`face: ${name}\n`);
16371671
}
16381672

16391673
export class TSConfigFile extends FILE {
1640-
constructor(path) {
1641-
super(path);
1642-
}
1643-
generate(tool) {
1644-
let json = {
1674+
generate(tool, typescript = true, javascript = false) {
1675+
const json = {
16451676
...tool.typescript.tsconfig,
16461677
compilerOptions: {
16471678
baseUrl: "./",
@@ -1650,36 +1681,72 @@ export class TSConfigFile extends FILE {
16501681
outDir: tool.modulesPath,
16511682
paths: {
16521683
},
1653-
lib: ["es2024"],
1684+
lib: ["es2024", "esnext.iterator"],
16541685
sourceMap: true,
1655-
target: "es2024",
1656-
...tool.typescript.tsconfig?.compilerOptions
1686+
target: "es2024"
16571687
},
16581688
files: [
16591689
]
16601690
}
1661-
var paths = json.compilerOptions.paths;
1662-
for (var result of tool.dtsFiles) {
1663-
var specifier = result.target;
1691+
const paths = json.compilerOptions.paths;
1692+
for (let result of tool.dtsFiles) {
1693+
let specifier = result.target;
16641694
if (tool.windows)
16651695
specifier = specifier.replaceAll("\\", "/");
16661696
specifier = tool.unresolvePrefix(specifier);
16671697
paths[specifier] = [ result.source.slice(0, -5) ];
16681698
}
1669-
for (var result of tool.tsFiles) {
1670-
var specifier = result.target.slice(0, -4);
1671-
if (tool.windows)
1672-
specifier = specifier.replaceAll("\\", "/");
1673-
specifier = tool.unresolvePrefix(specifier);
1674-
paths[specifier] = [ result.source.slice(0, -3) ];
1675-
json.files.push(result.source);
1699+
if (typescript) {
1700+
for (let result of tool.tsFiles) {
1701+
let specifier = result.target.slice(0, -4);
1702+
if (tool.windows)
1703+
specifier = specifier.replaceAll("\\", "/");
1704+
specifier = tool.unresolvePrefix(specifier);
1705+
paths[specifier] = [ result.source.slice(0, -3) ];
1706+
json.files.push(result.source);
1707+
}
1708+
}
1709+
if (javascript) {
1710+
const sources = TSConfigFile.filter(tool, tool.jsFiles);
1711+
for (let result of sources) {
1712+
let specifier = result.target.slice(0, -4);
1713+
if (tool.windows)
1714+
specifier = specifier.replaceAll("\\", "/");
1715+
specifier = tool.unresolvePrefix(specifier);
1716+
json.files.push(result.source);
1717+
}
1718+
1719+
json.compilerOptions = {
1720+
...json.compilerOptions,
1721+
allowJs: true,
1722+
checkJs: true,
1723+
noEmit: true,
1724+
strict: true,
1725+
noImplicitAny: false,
1726+
strictNullChecks: false
1727+
}
16761728
}
16771729
if ("zephyr" === tool.platform) {
1678-
paths["embedded:provider/builtin"] = [tool.tmpPath + tool.slash + "mc.devicetree.d.ts"];
1730+
paths["embedded:provider/builtin"] = [tool.tmpPath + tool.slash + "mc.devicetree"];
1731+
paths["mc/devicetree"] = [tool.tmpPath + tool.slash + "mc.devicetree.js"];
1732+
}
1733+
1734+
if (tool.typescript.tsconfig?.compilerOptions) {
1735+
json.compilerOptions = {
1736+
...json.compilerOptions,
1737+
...tool.typescript.tsconfig.compilerOptions
1738+
}
16791739
}
1740+
16801741
this.write(JSON.stringify(json, null, "\t"));
16811742
this.close();
16821743
}
1744+
static filter(tool, sources) {
1745+
const MODDABLE = tool.environment.MODDABLE;
1746+
const modules = MODDABLE + tool.slash + "modules" + tool.slash;
1747+
const build = MODDABLE + tool.slash + "build" + tool.slash;
1748+
return sources.filter(item => !item.source.startsWith(modules) && !item.source.startsWith(build));
1749+
}
16831750
}
16841751

16851752
export class PrerequisiteFile {
@@ -2314,6 +2381,12 @@ export class Tool extends TOOL {
23142381
this.xsbugLaunch = "log";
23152382
this.reportWarning(null, 0, "-l deprecated. use -dl instead.");
23162383
break;
2384+
case "-tc":
2385+
this.typeCheck = true;
2386+
break;
2387+
case "-lc":
2388+
this.lintCheck = true;
2389+
break;
23172390
default:
23182391
name = argv[argi];
23192392
let split = name.split("=");

tools/mcrun.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2016-2025 Moddable Tech, Inc.
2+
* Copyright (c) 2016-2026 Moddable Tech, Inc.
33
*
44
* This file is part of the Moddable SDK Tools.
55
*
@@ -485,8 +485,14 @@ export default class extends Tool {
485485

486486
if (this.tsFiles.length) {
487487
file = new TSConfigFile(this.modulesPath + this.slash + "tsconfig.json");
488-
file.generate(this);
488+
file.generate(this, true, false);
489+
}
490+
491+
if (this.jsFiles.length) {
492+
file = new TSConfigFile(this.modulesPath + this.slash + "tsconfig-js.json");
493+
file.generate(this, false, true);
489494
}
495+
490496
var path = this.tmpPath + this.slash + "makefile";
491497
file = new MakeFile(path);
492498
file.generate(this);

0 commit comments

Comments
 (0)