Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(deno): create deno system to run cli and compiler from deno
- Loading branch information
1 parent
4408ec1
commit b3d79c6
Showing
13 changed files
with
4,656 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
import type * as d from '../../declarations'; | ||
import type { Deno } from '../../../types/lib.deno'; | ||
import { buildError, catchError, flatOne, normalizePath, unique } from '@utils'; | ||
import { basename, dirname, expandGlob, isGlob, isAbsolute, join, resolve } from './deps'; | ||
|
||
export async function denoCopyTasks(deno: typeof Deno, copyTasks: Required<d.CopyTask>[], srcDir: string) { | ||
const results: d.CopyResults = { | ||
diagnostics: [], | ||
dirPaths: [], | ||
filePaths: [], | ||
}; | ||
|
||
try { | ||
copyTasks = flatOne(await Promise.all(copyTasks.map(task => processGlobs(task, srcDir)))); | ||
|
||
copyTasks = unique(copyTasks, task => task.dest); | ||
|
||
const allCopyTasks: d.CopyTask[] = []; | ||
|
||
// figure out all the file copy tasks we'll have | ||
// by digging down through any directory copy tasks | ||
while (copyTasks.length > 0) { | ||
const tasks = copyTasks.splice(0, 100); | ||
|
||
await Promise.all(tasks.map(copyTask => processCopyTask(deno, results, allCopyTasks, copyTask))); | ||
} | ||
|
||
// figure out which directories we'll need to make first | ||
const mkDirs = ensureDirs(allCopyTasks); | ||
|
||
try { | ||
await Promise.all(mkDirs.map(dir => deno.mkdir(dir, { recursive: true }))); | ||
} catch (mkDirErr) {} | ||
|
||
while (allCopyTasks.length > 0) { | ||
const tasks = allCopyTasks.splice(0, 100); | ||
|
||
await Promise.all(tasks.map(copyTask => deno.copyFile(copyTask.src, copyTask.dest))); | ||
} | ||
} catch (e) { | ||
catchError(results.diagnostics, e); | ||
} | ||
|
||
return results; | ||
} | ||
|
||
async function processGlobs(copyTask: Required<d.CopyTask>, srcDir: string): Promise<Required<d.CopyTask>[]> { | ||
return isGlob(copyTask.src) | ||
? await processGlobTask(copyTask, srcDir) | ||
: [ | ||
{ | ||
src: getSrcAbsPath(srcDir, copyTask.src), | ||
dest: copyTask.keepDirStructure ? join(copyTask.dest, copyTask.src) : copyTask.dest, | ||
warn: copyTask.warn, | ||
keepDirStructure: copyTask.keepDirStructure, | ||
}, | ||
]; | ||
} | ||
|
||
function getSrcAbsPath(srcDir: string, src: string) { | ||
if (isAbsolute(src)) { | ||
return src; | ||
} | ||
return join(srcDir, src); | ||
} | ||
|
||
async function processGlobTask(copyTask: Required<d.CopyTask>, srcDir: string): Promise<Required<d.CopyTask>[]> { | ||
const copyTasks: Required<d.CopyTask>[] = []; | ||
|
||
for await (const walkEntry of expandGlob(copyTask.src, { root: srcDir })) { | ||
const ct = createGlobCopyTask(copyTask, srcDir, walkEntry.name); | ||
copyTasks.push(ct); | ||
} | ||
return copyTasks; | ||
} | ||
|
||
function createGlobCopyTask(copyTask: Required<d.CopyTask>, srcDir: string, globRelPath: string): Required<d.CopyTask> { | ||
const dest = join(copyTask.dest, copyTask.keepDirStructure ? globRelPath : basename(globRelPath)); | ||
return { | ||
src: join(srcDir, globRelPath), | ||
dest, | ||
warn: copyTask.warn, | ||
keepDirStructure: copyTask.keepDirStructure, | ||
}; | ||
} | ||
|
||
async function processCopyTask(deno: typeof Deno, results: d.CopyResults, allCopyTasks: d.CopyTask[], copyTask: d.CopyTask) { | ||
try { | ||
copyTask.src = normalizePath(copyTask.src); | ||
copyTask.dest = normalizePath(copyTask.dest); | ||
|
||
// get the stats for this src to see if it's a directory or not | ||
const stats = await deno.stat(copyTask.src); | ||
if (stats.isDirectory) { | ||
// still a directory, keep diggin down | ||
if (!results.dirPaths.includes(copyTask.dest)) { | ||
results.dirPaths.push(copyTask.dest); | ||
} | ||
|
||
await processCopyTaskDirectory(deno, results, allCopyTasks, copyTask); | ||
} else if (!shouldIgnore(copyTask.src)) { | ||
// this is a file we should copy | ||
if (!results.filePaths.includes(copyTask.dest)) { | ||
results.filePaths.push(copyTask.dest); | ||
} | ||
|
||
allCopyTasks.push(copyTask); | ||
} | ||
} catch (e) { | ||
if (copyTask.warn !== false) { | ||
const err = buildError(results.diagnostics); | ||
err.messageText = e.message; | ||
} | ||
} | ||
} | ||
|
||
async function processCopyTaskDirectory(deno: typeof Deno, results: d.CopyResults, allCopyTasks: d.CopyTask[], copyTask: d.CopyTask) { | ||
try { | ||
for await (const dirEntry of deno.readDir(copyTask.src)) { | ||
const subCopyTask: d.CopyTask = { | ||
src: join(copyTask.src, dirEntry.name), | ||
dest: join(copyTask.dest, dirEntry.name), | ||
warn: copyTask.warn, | ||
}; | ||
|
||
await processCopyTask(deno, results, allCopyTasks, subCopyTask); | ||
} | ||
} catch (e) { | ||
catchError(results.diagnostics, e); | ||
} | ||
} | ||
|
||
function ensureDirs(copyTasks: d.CopyTask[]) { | ||
const mkDirs: string[] = []; | ||
|
||
copyTasks.forEach(copyTask => { | ||
addMkDir(mkDirs, dirname(copyTask.dest)); | ||
}); | ||
|
||
mkDirs.sort((a, b) => { | ||
const partsA = a.split('/').length; | ||
const partsB = b.split('/').length; | ||
|
||
if (partsA < partsB) return -1; | ||
if (partsA > partsB) return 1; | ||
if (a < b) return -1; | ||
if (a > b) return 1; | ||
return 0; | ||
}); | ||
|
||
return mkDirs; | ||
} | ||
|
||
function addMkDir(mkDirs: string[], destDir: string) { | ||
destDir = normalizePath(destDir); | ||
|
||
if (destDir === ROOT_DIR || destDir + '/' === ROOT_DIR || destDir === '') { | ||
return; | ||
} | ||
|
||
if (!mkDirs.includes(destDir)) { | ||
mkDirs.push(destDir); | ||
} | ||
} | ||
|
||
const ROOT_DIR = normalizePath(resolve('/')); | ||
|
||
function shouldIgnore(filePath: string) { | ||
filePath = filePath.trim().toLowerCase(); | ||
return IGNORE.some(ignoreFile => filePath.endsWith(ignoreFile)); | ||
} | ||
|
||
const IGNORE = ['.ds_store', '.gitignore', 'desktop.ini', 'thumbs.db']; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
import type * as d from '../../declarations'; | ||
import type TypeScript from 'typescript'; | ||
import { dependencies } from '../../compiler/sys/dependencies'; | ||
import { dirname, resolve } from 'path'; | ||
import { noop } from '@utils'; | ||
import type { Deno as DenoTypes } from '../../../types/lib.deno'; | ||
|
||
export const denoLoadTypeScript = (sys: d.CompilerSystem, rootDir: string, typeScriptPath: string): any => | ||
new Promise(async (promiseResolve, promiseReject) => { | ||
if (!sys) { | ||
promiseReject(`Unable to load TypeScript without Deno sys`); | ||
} else { | ||
try { | ||
const tsDep = dependencies.find(dep => dep.name === 'typescript'); | ||
|
||
const tsFilePath = typeScriptPath || sys.getLocalModulePath({ rootDir: rootDir, moduleId: tsDep.name, path: tsDep.main }); | ||
|
||
try { | ||
Deno.statSync(tsFilePath); | ||
} catch (e) { | ||
const tsUrl = sys.getRemoteModuleUrl({ moduleId: tsDep.name, version: tsDep.version, path: tsDep.main }); | ||
const rsp = await fetch(tsUrl); | ||
if (rsp.ok) { | ||
try { | ||
Deno.mkdirSync(dirname(tsFilePath), { recursive: true }); | ||
} catch (e) {} | ||
|
||
const content = await rsp.clone().text(); | ||
const encoder = new TextEncoder(); | ||
await Deno.writeFile(tsFilePath, encoder.encode(content)); | ||
} else { | ||
promiseReject(`unable to fetch: ${tsUrl}`); | ||
return; | ||
} | ||
} | ||
|
||
// ensure typescript compiler doesn't think it's nodejs | ||
(globalThis as any).process.browser = true; | ||
|
||
// fake cjs module.exports so typescript import gets added to it | ||
const orgModule = (globalThis as any).module; | ||
(globalThis as any).module = { exports: {} }; | ||
await import(tsFilePath); | ||
|
||
// get the typescript export from the fake cjs module.exports | ||
const importedTs = (globalThis as any).module.exports; | ||
|
||
if (orgModule) { | ||
(globalThis as any).module = orgModule; | ||
} else { | ||
delete (globalThis as any).module; | ||
} | ||
|
||
// create half-baked sys just to get us going | ||
// later on we'll wire up ts sys w/ the actual stencil sys | ||
const tsSys: TypeScript.System = { | ||
args: [], | ||
createDirectory: noop, | ||
directoryExists: p => { | ||
try { | ||
const s = Deno.statSync(p); | ||
return s.isDirectory; | ||
} catch (e) {} | ||
return false; | ||
}, | ||
exit: Deno.exit, | ||
fileExists: p => { | ||
try { | ||
const s = Deno.statSync(p); | ||
return s.isFile; | ||
} catch (e) {} | ||
return false; | ||
}, | ||
getCurrentDirectory: Deno.cwd, | ||
getDirectories: () => [], | ||
getExecutingFilePath: () => tsFilePath, | ||
newLine: '\n', | ||
readDirectory: () => [], | ||
readFile: (p, encoding) => { | ||
try { | ||
const decoder = new TextDecoder(encoding); | ||
const data = Deno.readFileSync(p); | ||
return decoder.decode(data); | ||
} catch (e) {} | ||
return undefined; | ||
}, | ||
resolvePath: p => resolve(p), | ||
useCaseSensitiveFileNames: Deno.build.os !== 'windows', | ||
write: noop, | ||
writeFile: noop, | ||
}; | ||
importedTs.sys = tsSys; | ||
|
||
promiseResolve(importedTs); | ||
} catch (e) { | ||
promiseReject(e); | ||
} | ||
} | ||
}); | ||
|
||
declare const Deno: typeof DenoTypes; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import { createTerminalLogger, ColorType, TerminalLoggerSys } from '../../compiler/sys/logger/terminal-logger'; | ||
import { bgRed, blue, bold, cyan, dim, gray, green, magenta, red, yellow } from './deps'; | ||
import type { Deno as DenoTypes } from '../../../types/lib.deno'; | ||
|
||
export const createDenoLogger = (c: { Deno: any }) => { | ||
let useColors = true; | ||
const deno: typeof DenoTypes = c.Deno; | ||
const minColumns = 60; | ||
const maxColumns = 120; | ||
|
||
const color = (msg: string, colorType: ColorType) => { | ||
if (useColors && !deno.noColor) { | ||
switch (colorType) { | ||
case 'bgRed': | ||
return bgRed(msg); | ||
case 'blue': | ||
return blue(msg); | ||
case 'bold': | ||
return bold(msg); | ||
case 'cyan': | ||
return cyan(msg); | ||
case 'dim': | ||
return dim(msg); | ||
case 'gray': | ||
return gray(msg); | ||
case 'green': | ||
return green(msg); | ||
case 'magenta': | ||
return magenta(msg); | ||
case 'red': | ||
return red(msg); | ||
case 'yellow': | ||
return yellow(msg); | ||
} | ||
} | ||
return msg; | ||
}; | ||
|
||
const cwd = () => deno.cwd(); | ||
|
||
const emoji = (e: string) => (deno.build.os !== 'windows' ? e : ''); | ||
|
||
const enableColors = (enableClrs: boolean) => (useColors = enableClrs); | ||
|
||
const getColumns = () => { | ||
const terminalWidth = (deno.stdout && (deno.stdout as any).columns) || 80; | ||
return Math.max(Math.min(maxColumns, terminalWidth), minColumns); | ||
}; | ||
|
||
const memoryUsage = () => -1; | ||
|
||
const relativePath = (_from: string, to: string) => to; | ||
|
||
const writeLogs = (logFilePath: string, log: string, append: boolean) => { | ||
const encoder = new TextEncoder(); | ||
const data = encoder.encode(log); | ||
deno.writeFileSync(logFilePath, data, { append }); | ||
}; | ||
|
||
const loggerSys: TerminalLoggerSys = { | ||
color, | ||
cwd, | ||
emoji, | ||
enableColors, | ||
getColumns, | ||
memoryUsage, | ||
relativePath, | ||
writeLogs, | ||
}; | ||
|
||
return createTerminalLogger(loggerSys); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { createRequire } from 'https://deno.land/std/node/module.ts'; | ||
import { join } from 'https://deno.land/std/path/mod.ts'; | ||
import * as nodeFs from 'https://deno.land/std/node/fs.ts'; | ||
import process from './deno-node-process'; | ||
import type { Deno as DenoTypes } from '../../../types/lib.deno'; | ||
|
||
|
||
// idk why the node compat doesn't come with stat and statSync on it?? | ||
// https://deno.land/std/node/fs.ts | ||
|
||
Object.assign(nodeFs, { | ||
stat: (...args: any[]) => { | ||
const path: string = args[0]; | ||
const cb = args.length > 2 ? args[2] : args[1] | ||
try { | ||
const s = Deno.statSync(path); | ||
cb && cb(null, { | ||
isFile: () => s.isFile, | ||
isDirectory: () => s.isDirectory, | ||
isSymbolicLink: () => s.isSymlink, | ||
size: s.size, | ||
}); | ||
} catch (e) { | ||
cb && cb(e); | ||
} | ||
}, | ||
statSync: (path: string) => { | ||
const s = Deno.statSync(path); | ||
return { | ||
isFile: () => s.isFile, | ||
isDirectory: () => s.isDirectory, | ||
isSymbolicLink: () => s.isSymlink, | ||
size: s.size, | ||
}; | ||
} | ||
}); | ||
|
||
export const applyNodeCompat = (opts: { fromDir: string }) => { | ||
(globalThis as any).process = process; | ||
|
||
const nodeRequire = createRequire(join(opts.fromDir, 'noop.js')); | ||
(globalThis as any).require = nodeRequire; | ||
}; | ||
|
||
declare const Deno: typeof DenoTypes; |
Oops, something went wrong.