Skip to content

Commit a6ac45e

Browse files
committed
Merge branch 'main' into toph/onramp
2 parents 7c719ae + b66e152 commit a6ac45e

File tree

12 files changed

+142
-57
lines changed

12 files changed

+142
-57
lines changed

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@
2626
"test": "concurrently npm:test:mocha npm:test:tsc npm:test:lint npm:test:prettier",
2727
"test:coverage": "c8 --check-coverage --lines 80 --per-file yarn test:mocha",
2828
"test:build": "rimraf test/build && cross-env npm_package_version=1.0.0-test node build.js --sourcemap --outdir=test/build \"{src,test}/**/*.{ts,js,css}\" --ignore \"test/input/**\" --ignore \"test/output/**\" --ignore \"test/preview/dashboard/**\" --ignore \"**/*.d.ts\" && cp -r templates test/build",
29-
"test:mocha": "yarn test:build && rimraf --glob test/.observablehq/cache test/input/build/*/.observablehq/cache && cross-env OBSERVABLE_TELEMETRY_DISABLE=1 TZ=America/Los_Angeles mocha --timeout 30000 -p \"test/build/test/**/*-test.js\"",
30-
"test:mocha:serial": "yarn test:build && rimraf --glob test/.observablehq/cache test/input/build/*/.observablehq/cache && cross-env OBSERVABLE_TELEMETRY_DISABLE=1 TZ=America/Los_Angeles mocha --timeout 30000 \"test/build/test/**/*-test.js\"",
29+
"test:mocha": "yarn test:build && rimraf --glob test/.observablehq/cache test/input/build/*/.observablehq/cache && cross-env OBSERVABLE_TELEMETRY_DISABLE=1 TZ=America/Los_Angeles mocha --timeout 30000 -p \"test/build/test/**/*-test.js\" && yarn test:annotate",
30+
"test:mocha:serial": "yarn test:build && rimraf --glob test/.observablehq/cache test/input/build/*/.observablehq/cache && cross-env OBSERVABLE_TELEMETRY_DISABLE=1 TZ=America/Los_Angeles mocha --timeout 30000 \"test/build/test/**/*-test.js\" && yarn test:annotate",
31+
"test:annotate": "yarn test:build && cross-env OBSERVABLE_ANNOTATE_FILES=true TZ=America/Los_Angeles mocha --timeout 30000 \"test/build/test/**/annotate.js\"",
3132
"test:lint": "eslint src test --max-warnings=0",
3233
"test:prettier": "prettier --check src test",
3334
"test:tsc": "tsc --noEmit",
@@ -83,6 +84,7 @@
8384
"mime": "^4.0.0",
8485
"minisearch": "^6.3.0",
8586
"open": "^10.1.0",
87+
"picocolors": "^1.1.1",
8688
"pkg-dir": "^8.0.0",
8789
"resolve.exports": "^2.0.2",
8890
"rollup": "^4.6.0",

src/commandInstruction.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
import {getObservableUiOrigin} from "./observableApiClient.js";
2-
import type {TtyColor} from "./tty.js";
32
import {bold, magenta} from "./tty.js";
43

54
export function commandInstruction(
65
command: string,
76
{
87
color = (s) => magenta(bold(s)),
98
env = process.env
10-
}: {color?: TtyColor | null; env?: Record<string, string | undefined>} = {}
9+
}: {
10+
color?: ((s: string) => string) | null;
11+
env?: Record<string, string | undefined>;
12+
} = {}
1113
): string {
12-
if (!color) color = (s) => s;
14+
color ??= (s) => s;
1315

1416
const prefix = env["npm_config_user_agent"]?.includes("yarn/")
1517
? "yarn observable"

src/javascript/annotate.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import {isPathImport} from "../path.js";
2+
3+
/**
4+
* Annotate a path to a local import or file so it can be reworked server-side.
5+
*/
6+
7+
const annotate = process.env["OBSERVABLE_ANNOTATE_FILES"];
8+
if (typeof annotate === "string" && annotate !== "true")
9+
throw new Error(`unsupported OBSERVABLE_ANNOTATE_FILES value: ${annotate}`);
10+
export default annotate
11+
? function (uri: string): string {
12+
return `${JSON.stringify(uri)}${isPathImport(uri) ? "/* observablehq-file */" : ""}`;
13+
}
14+
: JSON.stringify;

src/javascript/transpile.ts

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {isPathImport, relativePath, resolvePath, resolveRelativePath} from "../p
77
import {getModuleResolver} from "../resolvers.js";
88
import type {Params} from "../route.js";
99
import {Sourcemap} from "../sourcemap.js";
10+
import annotate from "./annotate.js";
1011
import type {FileExpression} from "./files.js";
1112
import {findFiles} from "./files.js";
1213
import type {ExportNode, ImportNode} from "./imports.js";
@@ -101,7 +102,7 @@ export async function transpileModule(
101102

102103
async function rewriteImportSource(source: StringLiteral) {
103104
const specifier = getStringLiteralValue(source);
104-
output.replaceLeft(source.start, source.end, JSON.stringify(await resolveImport(specifier)));
105+
output.replaceLeft(source.start, source.end, annotate(await resolveImport(specifier)));
105106
}
106107

107108
for (const {name, node} of findFiles(body, path, input)) {
@@ -111,17 +112,15 @@ export async function transpileModule(
111112
output.replaceLeft(
112113
source.start,
113114
source.end,
114-
`${JSON.stringify(
115+
`${
115116
info
116-
? {
117-
name: p,
118-
mimeType: mime.getType(name) ?? undefined,
119-
path: relativePath(servePath, resolveFile(name)),
120-
lastModified: info.mtimeMs,
121-
size: info.size
122-
}
123-
: p
124-
)}, import.meta.url`
117+
? `{"name":${JSON.stringify(p)},"mimeType":${JSON.stringify(
118+
mime.getType(name) ?? undefined
119+
)},"path":${annotate(relativePath(servePath, resolveFile(name)))},"lastModified":${JSON.stringify(
120+
info.mtimeMs
121+
)},"size":${JSON.stringify(info.size)}}`
122+
: JSON.stringify(p)
123+
}, import.meta.url`
125124
);
126125
}
127126

@@ -137,7 +136,7 @@ export async function transpileModule(
137136
if (isImportMetaResolve(node) && isStringLiteral(source)) {
138137
const value = getStringLiteralValue(source);
139138
const resolution = isPathImport(value) && !isJavaScript(value) ? resolveFile(value) : await resolveImport(value);
140-
output.replaceLeft(source.start, source.end, JSON.stringify(resolution));
139+
output.replaceLeft(source.start, source.end, annotate(resolution));
141140
}
142141
}
143142

@@ -205,7 +204,7 @@ function rewriteImportDeclarations(
205204
for (const node of declarations) {
206205
output.delete(node.start, node.end + +(output.input[node.end] === "\n"));
207206
specifiers.push(rewriteImportSpecifiers(node));
208-
imports.push(`import(${JSON.stringify(resolve(getStringLiteralValue(node.source as StringLiteral)))})`);
207+
imports.push(`import(${annotate(resolve(getStringLiteralValue(node.source as StringLiteral)))})`);
209208
}
210209
if (declarations.length > 1) {
211210
output.insertLeft(0, `const [${specifiers.join(", ")}] = await Promise.all([${imports.join(", ")}]);\n`);

src/node.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import type {AstNode, OutputChunk, Plugin, ResolveIdResult} from "rollup";
1313
import {rollup} from "rollup";
1414
import esbuild from "rollup-plugin-esbuild";
1515
import {prepareOutput, toOsPath} from "./files.js";
16+
import annotate from "./javascript/annotate.js";
1617
import type {ImportReference} from "./javascript/imports.js";
1718
import {isJavaScript, parseImports} from "./javascript/imports.js";
1819
import {parseNpmSpecifier, rewriteNpmImports} from "./npm.js";
@@ -86,7 +87,7 @@ function isBadCommonJs(specifier: string): boolean {
8687
}
8788

8889
function shimCommonJs(specifier: string, require: NodeRequire): string {
89-
return `export {${Object.keys(require(specifier))}} from ${JSON.stringify(specifier)};\n`;
90+
return `export {${Object.keys(require(specifier))}} from ${annotate(specifier)};\n`;
9091
}
9192

9293
async function bundle(

src/npm.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type {CallExpression} from "acorn";
55
import {simple} from "acorn-walk";
66
import {maxSatisfying, rsort, satisfies, validRange} from "semver";
77
import {isEnoent} from "./error.js";
8+
import annotate from "./javascript/annotate.js";
89
import type {ExportNode, ImportNode, ImportReference} from "./javascript/imports.js";
910
import {isImportMetaResolve, parseImports} from "./javascript/imports.js";
1011
import {parseProgram} from "./javascript/parse.js";
@@ -64,7 +65,7 @@ export function rewriteNpmImports(input: string, resolve: (s: string) => string
6465
const value = getStringLiteralValue(source);
6566
const resolved = resolve(value);
6667
if (resolved === undefined || value === resolved) return;
67-
output.replaceLeft(source.start, source.end, JSON.stringify(resolved));
68+
output.replaceLeft(source.start, source.end, annotate(resolved));
6869
}
6970

7071
// TODO Preserve the source map, but download it too.

src/rollup.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type {AstNode, OutputChunk, Plugin, ResolveIdResult} from "rollup";
66
import {rollup} from "rollup";
77
import esbuild from "rollup-plugin-esbuild";
88
import {getClientPath, getStylePath} from "./files.js";
9+
import annotate from "./javascript/annotate.js";
910
import type {StringLiteral} from "./javascript/source.js";
1011
import {getStringLiteralValue, isStringLiteral} from "./javascript/source.js";
1112
import {resolveNpmImport} from "./npm.js";
@@ -177,7 +178,7 @@ function importMetaResolve(path: string, resolveImport: ImportResolver): Plugin
177178
for (const source of resolves) {
178179
const specifier = getStringLiteralValue(source);
179180
const resolution = await resolveImport(specifier);
180-
if (resolution) output.replaceLeft(source.start, source.end, JSON.stringify(relativePath(path, resolution)));
181+
if (resolution) output.replaceLeft(source.start, source.end, annotate(relativePath(path, resolution)));
181182
}
182183

183184
return {code: String(output)};

src/tty.ts

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,22 @@
11
import {isatty} from "node:tty";
22
import * as clack from "@clack/prompts";
3+
import pc from "picocolors";
34
import type {ClackEffects} from "./clack.js";
45
import type {Logger} from "./logger.js";
56

6-
export const reset = color(0, 0);
7-
export const bold = color(1, 22);
8-
export const faint = color(2, 22);
9-
export const italic = color(3, 23);
10-
export const underline = color(4, 24);
11-
export const inverse = color(7, 27);
12-
export const strikethrough = color(9, 29);
13-
export const red = color(31, 39);
14-
export const green = color(32, 39);
15-
export const yellow = color(33, 39);
16-
export const blue = color(34, 39);
17-
export const magenta = color(35, 39);
18-
export const cyan = color(36, 39);
19-
20-
export type TtyColor = (text: string) => string;
21-
22-
function color(code: number, reset: number): TtyColor {
23-
return process.stdout.isTTY ? (text: string) => `\x1b[${code}m${text}\x1b[${reset}m` : String;
24-
}
7+
export const reset = pc.reset;
8+
export const bold = pc.bold;
9+
export const faint = pc.gray;
10+
export const italic = pc.italic;
11+
export const underline = pc.underline;
12+
export const inverse = pc.inverse;
13+
export const strikethrough = pc.strikethrough;
14+
export const red = pc.red;
15+
export const green = pc.green;
16+
export const yellow = pc.yellow;
17+
export const blue = pc.blue;
18+
export const magenta = pc.magenta;
19+
export const cyan = pc.cyan;
2520

2621
export interface TtyEffects {
2722
clack: ClackEffects;

templates/default/README.md.tmpl

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
# {{ appTitleHtml }}
22

3-
This is an [Observable Framework](https://observablehq.com/framework) app. To start the local preview server, run:
3+
This is an [Observable Framework](https://observablehq.com/framework/) app. To install the required dependencies, run:
4+
5+
```
6+
{{ installCommand }}
7+
```
8+
9+
Then, to start the local preview server, run:
410

511
```
612
{{ runCommand }} dev
@@ -31,13 +37,13 @@ A typical Framework project looks like this:
3137
└─ README.md
3238
```
3339

34-
**`src`** - This is the “source root” — where your source files live. Pages go here. Each page is a Markdown file. Observable Framework uses [file-based routing](https://observablehq.com/framework/routing), which means that the name of the file controls where the page is served. You can create as many pages as you like. Use folders to organize your pages.
40+
**`src`** - This is the “source root” — where your source files live. Pages go here. Each page is a Markdown file. Observable Framework uses [file-based routing](https://observablehq.com/framework/project-structure#routing), which means that the name of the file controls where the page is served. You can create as many pages as you like. Use folders to organize your pages.
3541

3642
**`src/index.md`** - This is the home page for your app. You can have as many additional pages as you’d like, but you should always have a home page, too.
3743

38-
**`src/data`** - You can put [data loaders](https://observablehq.com/framework/loaders) or static data files anywhere in your source root, but we recommend putting them here.
44+
**`src/data`** - You can put [data loaders](https://observablehq.com/framework/data-loaders) or static data files anywhere in your source root, but we recommend putting them here.
3945

40-
**`src/components`** - You can put shared [JavaScript modules](https://observablehq.com/framework/javascript/imports) anywhere in your source root, but we recommend putting them here. This helps you pull code out of Markdown files and into JavaScript modules, making it easier to reuse code across pages, write tests and run linters, and even share code with vanilla web applications.
46+
**`src/components`** - You can put shared [JavaScript modules](https://observablehq.com/framework/imports) anywhere in your source root, but we recommend putting them here. This helps you pull code out of Markdown files and into JavaScript modules, making it easier to reuse code across pages, write tests and run linters, and even share code with vanilla web applications.
4147

4248
**`observablehq.config.js`** - This is the [app configuration](https://observablehq.com/framework/config) file, such as the pages and sections in the sidebar navigation, and the app’s title.
4349

templates/empty/README.md.tmpl

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
# {{ appTitleHtml }}
22

3-
This is an [Observable Framework](https://observablehq.com/framework) app. To start the local preview server, run:
3+
This is an [Observable Framework](https://observablehq.com/framework/) app. To install the required dependencies, run:
4+
5+
```
6+
{{ installCommand }}
7+
```
8+
9+
Then, to start the local preview server, run:
410

511
```
612
{{ runCommand }} dev
@@ -31,13 +37,13 @@ A typical Framework project looks like this:
3137
└─ README.md
3238
```
3339

34-
**`src`** - This is the “source root” — where your source files live. Pages go here. Each page is a Markdown file. Observable Framework uses [file-based routing](https://observablehq.com/framework/routing), which means that the name of the file controls where the page is served. You can create as many pages as you like. Use folders to organize your pages.
40+
**`src`** - This is the “source root” — where your source files live. Pages go here. Each page is a Markdown file. Observable Framework uses [file-based routing](https://observablehq.com/framework/project-structure#routing), which means that the name of the file controls where the page is served. You can create as many pages as you like. Use folders to organize your pages.
3541

3642
**`src/index.md`** - This is the home page for your app. You can have as many additional pages as you’d like, but you should always have a home page, too.
3743

38-
**`src/data`** - You can put [data loaders](https://observablehq.com/framework/loaders) or static data files anywhere in your source root, but we recommend putting them here.
44+
**`src/data`** - You can put [data loaders](https://observablehq.com/framework/data-loaders) or static data files anywhere in your source root, but we recommend putting them here.
3945

40-
**`src/components`** - You can put shared [JavaScript modules](https://observablehq.com/framework/javascript/imports) anywhere in your source root, but we recommend putting them here. This helps you pull code out of Markdown files and into JavaScript modules, making it easier to reuse code across pages, write tests and run linters, and even share code with vanilla web applications.
46+
**`src/components`** - You can put shared [JavaScript modules](https://observablehq.com/framework/imports) anywhere in your source root, but we recommend putting them here. This helps you pull code out of Markdown files and into JavaScript modules, making it easier to reuse code across pages, write tests and run linters, and even share code with vanilla web applications.
4147

4248
**`observablehq.config.js`** - This is the [app configuration](https://observablehq.com/framework/config) file, such as the pages and sections in the sidebar navigation, and the app’s title.
4349

0 commit comments

Comments
 (0)