Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
41 changes: 41 additions & 0 deletions internals/lage-workers/eslint-worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { ESLint } from 'eslint';

/** @type {ESLint} */
let eslintInstance = null;

/** caches an ESLint instance for the worker */
function getEslintInstance(target) {
if (!eslintInstance) {
eslintInstance = new ESLint({
fix: false,
cache: false,
cwd: target.cwd,
});
}
return eslintInstance;
}

/** Workers should have a run function that gets called per package task */
async function run(data) {
const { target } = data;
const eslint = getEslintInstance(target);

// You can also use "options" to pass different files pattern to lint
// e.g. data.options.files; you'll need to then configure this inside
// lage.config.js's pipeline
const files = 'src/**/*.ts';
const results = await eslint.lintFiles(files);
const formatter = await eslint.loadFormatter('stylish');
const resultText = formatter.format(results);

// Output results to stdout
process.stdout.write(resultText + '\n');
if (results.some((r) => r.errorCount > 0)) {
// throw an error to indicate that this task has failed
throw new Error(`Linting failed with errors`);
}
}

// The module export is picked up by `lage` to run inside a worker, and the
// module's state is preserved from target run to target run.
export default run;
17 changes: 17 additions & 0 deletions internals/lage-workers/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "@repo/lage-workers",
"version": "0.0.0",
"private": true,
"type": "module",
"engines": {
"node": ">=18"
},
"exports": {
"./eslint-worker": "./eslint-worker.js",
"./tsc-worker": "./tsc-worker.js"
},
"dependencies": {
"eslint": "^9.18.0",
"typescript": "^5.7.3"
}
}
128 changes: 128 additions & 0 deletions internals/lage-workers/tsc-worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import ts from 'typescript';
import path from 'node:path';
import { existsSync } from 'node:fs';

// Save the previously run ts.program to be fed inside the next call
let oldProgram;

let compilerHost;

/** this is the patch to ts.compilerHost that retains sourceFiles in a Map **/
function createCompilerHost(compilerOptions) {
const host = ts.createCompilerHost(compilerOptions, true);
const sourceFiles = new Map();
const originalGetSourceFile = host.getSourceFile;

// monkey patch host to cache source files
host.getSourceFile = (
fileName,
languageVersion,
onError,
shouldCreateNewSourceFile,
) => {
if (sourceFiles.has(fileName)) {
return sourceFiles.get(fileName);
}

const sourceFile = originalGetSourceFile(
fileName,
languageVersion,
onError,
shouldCreateNewSourceFile,
);

sourceFiles.set(fileName, sourceFile);

return sourceFile;
};

return host;
}

async function tsc(data) {
const { target } = data; // Lage target data

const tsconfigJsonFile = path.join(target.cwd, 'tsconfig.json');

if (!existsSync(tsconfigJsonFile)) {
console.log(`this package (${target.cwd}) has no tsconfig.json, skipping work!`);
return;
}

// Parse tsconfig
const configParserHost = parseConfigHostFromCompilerHostLike(compilerHost ?? ts.sys);
const parsedCommandLine = ts.getParsedCommandLineOfConfigFile(
tsconfigJsonFile,
{},
configParserHost,
);
if (!parsedCommandLine) {
throw new Error('Could not parse tsconfig.json');
}
const compilerOptions = parsedCommandLine.options;

// Creating compilation host program
compilerHost = compilerHost ?? createCompilerHost(compilerOptions);

// The re-use of oldProgram is a trick we all learned from gulp-typescript, credit to ivogabe
// @see https://github.com/ivogabe/gulp-typescript
const program = ts.createProgram(
parsedCommandLine.fileNames,
compilerOptions,
compilerHost,
oldProgram,
);

oldProgram = program;

const errors = {
semantics: program.getSemanticDiagnostics(),
declaration: program.getDeclarationDiagnostics(),
syntactic: program.getSyntacticDiagnostics(),
global: program.getGlobalDiagnostics(),
};

const allErrors = [];

try {
program.emit();
} catch (error) {
console.log(error.messageText);
throw new Error('Encountered errors while emitting');
}

let hasErrors = false;

for (const kind of Object.keys(errors)) {
for (const diagnostics of errors[kind]) {
hasErrors = true;
allErrors.push(diagnostics);
}
}

if (hasErrors) {
console.log(ts.formatDiagnosticsWithColorAndContext(allErrors, compilerHost));
throw new Error('Failed to compile');
} else {
console.log('Compiled successfully\n');
return;
}
}

function parseConfigHostFromCompilerHostLike(host) {
return {
fileExists: (f) => host.fileExists(f),
readDirectory(root, extensions, excludes, includes, depth) {
return host.readDirectory(root, extensions, excludes, includes, depth);
},
readFile: (f) => host.readFile(f),
useCaseSensitiveFileNames: host.useCaseSensitiveFileNames,
getCurrentDirectory: host.getCurrentDirectory,
onUnRecoverableConfigFileDiagnostic: (d) => {
throw new Error(ts.flattenDiagnosticMessageText(d.messageText, '\n'));
},
trace: host.trace,
};
}

export default tsc;
2 changes: 1 addition & 1 deletion internals/tsconfig/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"esm": true
},
"compilerOptions": {
"lib": ["es2023"],
"lib": ["es2023", "WebWorker"],
"skipLibCheck": false,
"esModuleInterop": true
}
Expand Down
23 changes: 23 additions & 0 deletions lage.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module.exports = {
pipeline: {
attw: {
dependsOn: ['tsc'],
outputs: [],
},
tsc: {
type: 'worker',
options: {
worker: require.resolve('@repo/lage-workers/tsc-worker'),
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dummy drop to try, but I think here we want to run tshy.

},
dependsOn: ['^tsc'],
outputs: ['dist/**'],
},
lint: {
type: 'worker',
options: {
worker: require.resolve('@repo/lage-workers/eslint-worker'),
},
},
},
npmClient: 'yarn',
};
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
"devDependencies": {
"@eslint/js": "^9.18.0",
"@repo/hoist-peer-dependencies": "workspace:*",
"@repo/lage-workers": "workspace:*",
"@repo/tsconfig": "workspace:*",
"@types/node": "^22.10.5",
"@vitest/coverage-v8": "^3.0.2",
Expand All @@ -86,6 +87,7 @@
"globals": "^15.14.0",
"globby": "^14.0.2",
"husky": "^9.1.7",
"lage": "^2.12.6",
"lerna": "^8.1.9",
"nano-staged": "^0.8.0",
"prettier": "^3.4.2",
Expand Down
86 changes: 86 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,7 @@ __metadata:
dependencies:
"@eslint/js": "npm:^9.18.0"
"@repo/hoist-peer-dependencies": "workspace:*"
"@repo/lage-workers": "workspace:*"
"@repo/tsconfig": "workspace:*"
"@types/node": "npm:^22.10.5"
"@vitest/coverage-v8": "npm:^3.0.2"
Expand All @@ -745,6 +746,7 @@ __metadata:
globals: "npm:^15.14.0"
globby: "npm:^14.0.2"
husky: "npm:^9.1.7"
lage: "npm:^2.12.6"
lerna: "npm:^8.1.9"
nano-staged: "npm:^0.8.0"
prettier: "npm:^3.4.2"
Expand Down Expand Up @@ -1509,6 +1511,15 @@ __metadata:
languageName: unknown
linkType: soft

"@repo/lage-workers@workspace:*, @repo/lage-workers@workspace:internals/lage-workers":
version: 0.0.0-use.local
resolution: "@repo/lage-workers@workspace:internals/lage-workers"
dependencies:
eslint: "npm:^9.18.0"
typescript: "npm:^5.7.3"
languageName: unknown
linkType: soft

"@repo/tsconfig@workspace:*, @repo/tsconfig@workspace:internals/tsconfig":
version: 0.0.0-use.local
resolution: "@repo/tsconfig@workspace:internals/tsconfig"
Expand Down Expand Up @@ -4360,6 +4371,65 @@ __metadata:
languageName: node
linkType: hard

"glob-hasher-darwin-arm64@npm:1.4.2":
version: 1.4.2
resolution: "glob-hasher-darwin-arm64@npm:1.4.2"
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard

"glob-hasher-darwin-x64@npm:1.4.2":
version: 1.4.2
resolution: "glob-hasher-darwin-x64@npm:1.4.2"
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard

"glob-hasher-linux-x64-gnu@npm:1.4.2":
version: 1.4.2
resolution: "glob-hasher-linux-x64-gnu@npm:1.4.2"
conditions: os=linux & cpu=x64 & libc=glibc
languageName: node
linkType: hard

"glob-hasher-win32-arm64-msvc@npm:1.4.2":
version: 1.4.2
resolution: "glob-hasher-win32-arm64-msvc@npm:1.4.2"
conditions: os=win32 & cpu=arm64
languageName: node
linkType: hard

"glob-hasher-win32-x64-msvc@npm:1.4.2":
version: 1.4.2
resolution: "glob-hasher-win32-x64-msvc@npm:1.4.2"
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard

"glob-hasher@npm:^1.4.2":
version: 1.4.2
resolution: "glob-hasher@npm:1.4.2"
dependencies:
glob-hasher-darwin-arm64: "npm:1.4.2"
glob-hasher-darwin-x64: "npm:1.4.2"
glob-hasher-linux-x64-gnu: "npm:1.4.2"
glob-hasher-win32-arm64-msvc: "npm:1.4.2"
glob-hasher-win32-x64-msvc: "npm:1.4.2"
dependenciesMeta:
glob-hasher-darwin-arm64:
optional: true
glob-hasher-darwin-x64:
optional: true
glob-hasher-linux-x64-gnu:
optional: true
glob-hasher-win32-arm64-msvc:
optional: true
glob-hasher-win32-x64-msvc:
optional: true
checksum: 10/7d21697e63cc43f6edcec52b88ed0cc877011edaca1446c0fb1fb2efb3b7bd26f9900dfde7a7ab62ee425c6bd8da359c0a3d34997c9916de6b887029ca2995d8
languageName: node
linkType: hard

"glob-parent@npm:6.0.2, glob-parent@npm:^6.0.2":
version: 6.0.2
resolution: "glob-parent@npm:6.0.2"
Expand Down Expand Up @@ -5351,6 +5421,22 @@ __metadata:
languageName: node
linkType: hard

"lage@npm:^2.12.6":
version: 2.12.6
resolution: "lage@npm:2.12.6"
dependencies:
fsevents: "npm:~2.3.2"
glob-hasher: "npm:^1.4.2"
dependenciesMeta:
fsevents:
optional: true
bin:
lage: dist/lage.js
lage-server: dist/lage-server.js
checksum: 10/1b22aaab3170a1a6c83ddddf5ab6569e73e24c66e349abffcc12f812071517d32fd4860f7eb530c6db9e9aac56348ebe29415367dfb45377f8b93c3c514c91b5
languageName: node
linkType: hard

"lerna@npm:^8.1.9":
version: 8.1.9
resolution: "lerna@npm:8.1.9"
Expand Down
Loading