@mieweb/wikigdrive
Version:
Google Drive to MarkDown synchronization
138 lines (137 loc) • 4.7 kB
JavaScript
import { execFile } from 'node:child_process';
import process from 'node:process';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { execAsync } from './utils.js';
function isResolveError(info) {
return 'error' in info && typeof info.error === 'string';
}
let checkedDenoInstall = false;
const DENO_BINARY = process.platform === 'win32' ? 'deno.exe' : 'deno';
export async function resolveDeno(id, cwd) {
if (!checkedDenoInstall) {
try {
await execAsync(`${DENO_BINARY} --version`, { cwd });
checkedDenoInstall = true;
}
catch {
throw new Error(`Deno binary could not be found. Install Deno to resolve this error.`);
}
}
// There is no JS-API in Deno to get the final file path in Deno's
// cache directory. The `deno info` command reveals that information
// though, so we can use that.
const output = await new Promise((resolve, reject) => {
execFile(DENO_BINARY, ['info', '--json', id], { cwd }, (error, stdout) => {
if (error) {
if (String(error).includes('Integrity check failed')) {
reject(error);
}
else {
resolve(null);
}
}
else
resolve(stdout);
});
});
if (output === null)
return null;
const json = JSON.parse(output);
const actualId = json.roots[0];
// Find the final resolved cache path. First, we need to check
// if the redirected specifier, which represents the final specifier.
// This is often used for `http://` imports where a example-server-hono can do
// redirects.
const redirected = json.redirects[actualId] ?? actualId;
// Find the module information based on the redirected speciffier
const mod = json.modules.find((info) => info.specifier === redirected);
if (mod === undefined)
return null;
if ('error' in mod &&
mod.error.indexOf('Expected a JavaScript or TypeScript module, but identified a Css module.') > -1) {
return {
id: actualId.replace('file:', ''),
kind: 'css',
loader: 'css',
dependencies: [],
};
}
// Specifier not found by deno
if (isResolveError(mod)) {
return null;
}
if (mod.kind === 'esm') {
return {
id: mod.local,
kind: mod.kind,
loader: mod.mediaType,
dependencies: mod.dependencies,
};
}
else if (mod.kind === 'npm') {
return {
id: mod.npmPackage,
kind: mod.kind,
loader: null,
dependencies: [],
};
}
else if (mod.kind === 'external') {
// Let vite handle this
return null;
}
throw new Error(`Unsupported: ${JSON.stringify(mod, null, 2)}`);
}
export async function resolveViteSpecifier(id, cache, root, importer) {
// Resolve import map
const origId = id;
if (!id.startsWith('.') && !id.startsWith('/')) {
try {
id = globalThis[Symbol.for("import-meta-ponyfill-esmodule")](import.meta).resolve(id);
}
catch {
// Ignore: not resolvable
}
}
if (importer && isDenoSpecifier(importer)) {
const { resolved: parent } = parseDenoSpecifier(importer);
const cached = cache.get(parent);
if (cached === undefined)
return;
const found = cached.dependencies.find((dep) => dep.specifier === origId);
if (found === undefined)
return;
// Check if we need to continue resolution
id = found.code.specifier;
if (id.startsWith('file://')) {
return fileURLToPath(id);
}
}
const resolved = cache.get(id) ?? await resolveDeno(id, root);
// Deno cannot resolve this
if (resolved === null)
return;
if (resolved.kind === 'npm') {
return null;
}
cache.set(resolved.id, resolved);
// Vite can load this
if (resolved.loader === null ||
resolved.id.startsWith(path.resolve(root)) &&
!path.relative(root, resolved.id).startsWith('.')) {
return resolved.id;
}
// We must load it
return toDenoSpecifier(resolved.loader, id, resolved.id);
}
export function isDenoSpecifier(str) {
return str.startsWith('\0deno');
}
export function toDenoSpecifier(loader, id, resolved) {
return `\0deno::${loader}::${id}::${resolved}`;
}
export function parseDenoSpecifier(spec) {
const [_, loader, id, resolved] = spec.split('::');
return { loader: loader, id, resolved };
}