Skip to content

Commit

Permalink
esm-monaco: Refactor typescript work for supporting types from CDN
Browse files Browse the repository at this point in the history
  • Loading branch information
ije committed Feb 4, 2024
1 parent d43b172 commit 0371102
Show file tree
Hide file tree
Showing 11 changed files with 1,073 additions and 476 deletions.
2 changes: 2 additions & 0 deletions packages/esm-monaco/src/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export async function init(options: InitOptions = {}) {
}

if (options.vfs) {
options.vfs.bindMonaco(monaco);
Reflect.set(monaco.editor, "vfs", options.vfs);
try {
const list = await options.vfs.list();
Expand Down Expand Up @@ -120,6 +121,7 @@ export async function init(options: InitOptions = {}) {
"trimAutoWhitespace",
"wordWrap",
"wordWrapColumn",
"wrappingIndent",
];
for (const attrName of this.getAttributeNames()) {
const key = optionKeys.find((k) => k.toLowerCase() === attrName);
Expand Down
111 changes: 111 additions & 0 deletions packages/esm-monaco/src/import-map.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
export interface ImportMap {
$src?: string;
$support?: boolean;
$baseURL: string;
imports: Record<string, string>;
scopes: Record<string, ImportMap["imports"]>;
}

export function blankImportMap(): ImportMap {
return {
$baseURL: "file:///",
imports: {},
scopes: {},
};
}

export function isBlank(importMap: ImportMap) {
return (
Object.keys(importMap.imports).length === 0 &&
Object.keys(importMap.scopes).length === 0
);
}

function matchImports(specifier: string, imports: ImportMap["imports"]) {
if (specifier in imports) {
return imports[specifier];
}
for (const [k, v] of Object.entries(imports)) {
if (k.endsWith("/") && specifier.startsWith(k)) {
return v + specifier.slice(k.length);
}
}
return null;
}

export function resolve(
importMap: ImportMap,
specifier: string,
scriptUrlRaw: string,
) {
const { $baseURL, imports, scopes } = importMap;
const scriptUrl = new URL(scriptUrlRaw);
const sameOriginScopes = Object.entries(scopes)
.map(([scope, imports]) => [new URL(scope, $baseURL), imports] as const)
.filter(([scopeUrl]) => scopeUrl.origin === scriptUrl.origin)
.sort(([a], [b]) =>
b.pathname.split("/").length - a.pathname.split("/").length
);
if (sameOriginScopes.length > 0) {
for (const [scopeUrl, scopeImports] of sameOriginScopes) {
if (scriptUrl.pathname.startsWith(scopeUrl.pathname)) {
const match = matchImports(specifier, scopeImports);
if (match) {
return new URL(match, scopeUrl);
}
}
}
}
const match = matchImports(specifier, imports);
if (match) {
return new URL(match, scriptUrl);
}
return new URL(specifier, scriptUrl);
}

export function parseImportMapFromJson(
json: string,
baseURL?: string,
): ImportMap {
const importMap: ImportMap = {
$support: globalThis.HTMLScriptElement?.supports?.("importmap"),
$baseURL: new URL(baseURL ?? ".", "file:///").href,
imports: {},
scopes: {},
};
const v = JSON.parse(json);
if (isObject(v)) {
const { imports, scopes } = v;
if (isObject(imports)) {
validateImports(imports);
importMap.imports = imports as ImportMap["imports"];
}
if (isObject(scopes)) {
validateScopes(scopes);
importMap.scopes = scopes as ImportMap["scopes"];
}
}
return importMap;
}

function validateImports(imports: Record<string, unknown>) {
for (const [k, v] of Object.entries(imports)) {
if (!v || typeof v !== "string") {
delete imports[k];
}
}
}

function validateScopes(imports: Record<string, unknown>) {
for (const [k, v] of Object.entries(imports)) {
if (isObject(v)) {
validateImports(v);
} else {
delete imports[k];
}
}
}

function isObject(v: unknown): v is Record<string, unknown> {
return v && typeof v === "object" && !Array.isArray(v);
}
43 changes: 41 additions & 2 deletions packages/esm-monaco/src/lsp/json/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,53 @@ import type { SchemaConfiguration } from "vscode-json-languageservice";

export const schemas: SchemaConfiguration[] = [
{
uri:
"https://raw.githubusercontent.com/denoland/vscode_deno/main/schemas/import_map.schema.json",
uri: "//",
fileMatch: [
"import_map.json",
"import-map.json",
"importmap.json",
"importMap.json",
],
schema: {
$schema: "http://json-schema.org/draft-07/schema#",
$id:
"https://github.com/denoland/vscode_deno/blob/main/schemas/import_map.schema.json",
title: "An Import Map",
description:
"An import map which is used to remap imports when modules are loaded.",
type: "object",
properties: {
imports: {
description: "A map of specifiers to their remapped specifiers.",
type: "object",
properties: {
"@jsxImportSource": {
description: "The key is the specifier for JSX runtime.",
type: "string",
},
},
additionalProperties: {
description:
"The key is the specifier or partial specifier to match, with a value that represents the target specifier.",
type: "string",
},
},
scopes: {
description:
"Define a scope which remaps a specifier in only a specified scope",
type: "object",
additionalProperties: {
description: "A definition of a scoped remapping.",
type: "object",
additionalProperties: {
description:
"The key is the specifier or partial specifier to match within the referring scope, with a value that represents the target specifier.",
type: "string",
},
},
},
},
},
},
{
uri: "https://json.schemastore.org/tsconfig",
Expand Down
Loading

0 comments on commit 0371102

Please sign in to comment.