@monimesl/dot-cli 0.1.0-alpha.7 → 0.1.0-alpha.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -9
- package/dist/cli.js +158 -71
- package/dist/cli.js.map +1 -1
- package/dist/{server-DFOUQnLA.js → server-BfPAnsSf.js} +34 -81
- package/dist/{server-DFOUQnLA.js.map → server-BfPAnsSf.js.map} +1 -1
- package/dist/templates/showcase/.env +1 -1
- package/dist/templates/showcase/dot.ts +2 -0
- package/dist/templates/showcase/pages/cache/controller.ts +45 -0
- package/dist/templates/showcase/pages/cache/state.ts +17 -0
- package/dist/templates/showcase/pages/cache/view.dml +36 -0
- package/dist/templates/showcase/pages/carousel/controller.ts +33 -3
- package/dist/templates/showcase/pages/carousel/state.ts +61 -24
- package/dist/templates/showcase/pages/carousel/view.dml +18 -15
- package/dist/templates/showcase/pages/device/controller.ts +22 -12
- package/dist/templates/showcase/pages/device/state.ts +1 -1
- package/dist/templates/showcase/pages/device/view.dml +13 -8
- package/dist/templates/showcase/pages/feedback/controller.ts +6 -1
- package/dist/templates/showcase/pages/feedback/state.ts +1 -0
- package/dist/templates/showcase/pages/feedback/view.dml +23 -23
- package/dist/templates/showcase/pages/files/controller.ts +33 -14
- package/dist/templates/showcase/pages/files/state.ts +10 -2
- package/dist/templates/showcase/pages/files/view.dml +29 -14
- package/dist/templates/showcase/pages/filters/controller.ts +11 -12
- package/dist/templates/showcase/pages/filters/state.ts +107 -61
- package/dist/templates/showcase/pages/filters/view.dml +18 -17
- package/dist/templates/showcase/pages/forms/controller.ts +36 -12
- package/dist/templates/showcase/pages/forms/state.ts +25 -6
- package/dist/templates/showcase/pages/forms/view.dml +28 -18
- package/dist/templates/showcase/pages/images/controller.ts +26 -0
- package/dist/templates/showcase/pages/images/state.ts +3 -0
- package/dist/templates/showcase/pages/images/view.dml +18 -3
- package/dist/templates/showcase/pages/index/state.ts +26 -14
- package/dist/templates/showcase/pages/index/view.dml +2 -2
- package/dist/templates/showcase/pages/layout/controller.ts +36 -2
- package/dist/templates/showcase/pages/layout/state.ts +22 -2
- package/dist/templates/showcase/pages/layout/view.dml +45 -22
- package/dist/templates/showcase/pages/lists/controller.ts +12 -11
- package/dist/templates/showcase/pages/lists/state.ts +87 -66
- package/dist/templates/showcase/pages/lists/view.dml +15 -15
- package/dist/templates/showcase/pages/navigation/controller.ts +2 -1
- package/dist/templates/showcase/pages/navigation/view.dml +5 -5
- package/dist/templates/showcase/pages/network/controller.ts +68 -24
- package/dist/templates/showcase/pages/network/state.ts +13 -6
- package/dist/templates/showcase/pages/network/view.dml +29 -18
- package/dist/templates/showcase/pages/payments/controller.ts +16 -12
- package/dist/templates/showcase/pages/payments/state.ts +3 -0
- package/dist/templates/showcase/pages/payments/view.dml +22 -12
- package/dist/templates/showcase/pages/secrets/controller.ts +43 -0
- package/dist/templates/showcase/pages/secrets/state.ts +5 -0
- package/dist/templates/showcase/pages/secrets/view.dml +24 -0
- package/dist/templates/showcase/pages/state/state.ts +1 -1
- package/dist/templates/showcase/pages/state/view.dml +9 -9
- package/dist/templates/showcase/pages/text/controller.ts +3 -3
- package/dist/templates/showcase/pages/text/state.ts +6 -5
- package/dist/templates/showcase/pages/text/view.dml +22 -13
- package/dist/templates/showcase/statics/logo.png +0 -0
- package/package.json +6 -6
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","names":[],"sources":["../src/report.ts","../src/create/template.ts","../src/create/create.ts","../src/create/page.ts","../src/create/package-manager.ts","../src/device/developer-key.ts","../src/device/server.ts","../src/dev.ts","../src/cli.ts"],"sourcesContent":["import { DmlParseError } from '@monimesl/dot-dml-parser';\nimport type { BuildResult } from '@monimesl/dot-compiler';\nimport { CompileError } from '@monimesl/dot-compiler';\nimport { ScriptParseError } from '@monimesl/dot-script-parser';\n\n/** A mistake in how a command was used, which its message alone explains. */\nexport class CommandError extends Error {\n override readonly name = 'CommandError';\n}\n\nexport function describeError(error: unknown): string {\n const known =\n error instanceof CommandError ||\n error instanceof CompileError ||\n error instanceof DmlParseError ||\n error instanceof ScriptParseError;\n if (known) {\n return error.message;\n }\n if ((error as { code?: string }).code?.startsWith('ERR_PARSE_ARGS')) {\n // Node's message goes on to explain `--` for positional arguments, which is noise here.\n const [firstSentence] = (error as Error).message.split('. ');\n return `${firstSentence}. See dotctl --help.`;\n }\n return error instanceof Error ? (error.stack ?? error.message) : String(error);\n}\n\nexport function printWarnings({ warnings }: BuildResult): void {\n for (const warning of warnings) {\n console.warn(`warning: ${warning.message}`);\n }\n}\n\nexport function printBuildTime(startedAt: number): void {\n console.log(`Built in ${Math.round(performance.now() - startedAt)}ms`);\n}\n","// `starter` is one page to build on; `showcase` is a page for each feature, to read.\nexport const TEMPLATES = ['starter', 'showcase'] as const;\n\nexport type Template = (typeof TEMPLATES)[number];\n\nexport function isTemplate(name: string): name is Template {\n return (TEMPLATES as readonly string[]).includes(name);\n}\n\nexport interface TemplateOptions {\n name: string;\n packageName: string;\n dotVersion: string;\n workspace: boolean;\n template: Template;\n}\n\nexport const PAGE_FILES: Readonly<Record<string, string>> = {\n 'view.dml': '<View>\\n\\t<Text>My new page</Text>\\n</View>\\n',\n 'state.ts': 'export default {};\\n',\n 'controller.ts': `import state from './state';\n\nexport default definePage({\n initialState: state,\n});\n`,\n};\n\n// The dot itself is copied from the template's example; these are the files around it.\nexport const TEMPLATE_FILES = ['dot.ts', 'pages', 'statics', '.env'] as const;\n\n// For editors that follow `$schema`, they can check dot.json and complete its fields.\nconst DOT_JSON_SCHEMA = './node_modules/@monimesl/dot-runtime/dot.schema.json';\n\nconst json = (value: unknown) => `${JSON.stringify(value, null, 2)}\\n`;\n\nexport function projectFiles(options: TemplateOptions): Record<string, string> {\n return {\n 'package.json': packageJson(options),\n 'tsconfig.json': tsconfig(options),\n '.gitignore': 'node_modules/\\ndist/\\n# Types generated by dotctl\\n.dot/\\n',\n 'dot.json': dotJson(options),\n 'README.md': readme(options),\n };\n}\n\nfunction packageJson({ packageName, dotVersion, workspace }: TemplateOptions): string {\n const conditions = workspace ? ' --conditions @monimesl/dot-source' : '';\n return json({\n name: packageName,\n private: true,\n type: 'module',\n scripts: {\n dev: `dotctl dev${conditions}`,\n build: `dotctl build${conditions}`,\n typecheck: 'tsc --noEmit',\n },\n dependencies: { '@monimesl/dot-runtime': dotVersion },\n devDependencies: { '@monimesl/dot-cli': dotVersion, typescript: '^5.9.3' },\n });\n}\n\nfunction tsconfig({ workspace }: TemplateOptions): string {\n const compilerOptions: Record<string, unknown> = {\n target: 'ES2022',\n module: 'ESNext',\n moduleResolution: 'bundler',\n lib: ['ES2022'],\n types: ['@monimesl/dot-runtime/globals'],\n strict: true,\n noEmit: true,\n skipLibCheck: true,\n verbatimModuleSyntax: true,\n };\n if (workspace) {\n compilerOptions.customConditions = ['@monimesl/dot-source'];\n }\n return json({\n compilerOptions,\n include: ['dot.ts', 'pages', '.dot/pages.d.ts', '.dot/env.d.ts'],\n });\n}\n\nfunction dotJson({ name, packageName, template }: TemplateOptions): string {\n const dot: Record<string, unknown> = {\n $schema: DOT_JSON_SCHEMA,\n id: `com.example.${idWord(packageName)}`,\n name,\n version: '0.1.0',\n };\n if (template === 'showcase') {\n // The showcase's network page reaches these.\n dot.network = { hosts: ['api.github.com', 'avatars.githubusercontent.com', 'httpbin.org'] };\n }\n return json(dot);\n}\n\nconst TEMPLATE_READMES: Readonly<Record<Template, string>> = {\n starter: `It starts with one page, \\`index\\`: a greeting from \\`.env\\` and a count to tap. Change it,\nand add pages as the dot grows. \\`npx dotctl create my-tour --template showcase\\` makes a dot with a\npage for each feature, to read how each is done.`,\n showcase: `It starts as a showcase: \\`index\\` lists a page for each feature, from layout, text, images and\ninputs to state, navigation, and Dash's alerts, network and files. Read a page to see how it's\ndone, then delete the ones you don't need, from \\`pages/\\` and from \\`dot.ts\\`.`,\n};\n\nfunction readme({ name, template }: TemplateOptions): string {\n return `# ${name}\n\nA dot: a mini app that runs inside Dash.\n\n\\`\\`\\`sh\nnpm install\nnpm run dev # run it in the simulator; it reloads on every change\nnpm run build # build it for Dash\n\\`\\`\\`\n\nEach page is a folder under \\`pages/\\` with three files: \\`view.dml\\` (what it shows),\n\\`state.ts\\` (what it starts with) and \\`controller.ts\\` (how it changes). A page's markup is one\nroot \\`<View>\\` with everything else inside it. \\`npx dotctl create page <path>\\` adds a page, such\nas \\`cart/checkout\\`, with the three files to start from, and lists it in \\`dot.ts\\`. The first page\nthere is where the dot starts.\n\n\\`dot.json\\` describes the dot to Dash. Change its \\`id\\` from \\`com.example.…\\` to a reverse domain\nname you own before you publish: it can't change afterwards. Its \\`network.hosts\\` lists every host\nthe dot sends requests to with \\`dash.network\\`; requests to any other fail. Your editor completes\nthe other fields from the schema it points to.\n\n\\`.env\\` holds public configuration, which the service reads as \\`env.GREETING\\`. Every variable\nthere is built into the dot, so anyone who has it can read them: never put a secret there.\n\\`.env.development\\` and \\`.env.production\\` override it for \\`dev\\` and \\`build\\`.\n\n${TEMPLATE_READMES[template]}\n`;\n}\n\n// A word of the dot's id: the package name, which is already lowercase letters, digits and inner\n// hyphens, cut to the 63 characters a host name allows.\nfunction idWord(packageName: string): string {\n return packageName.slice(0, 63).replace(/-+$/, '');\n}\n","import { existsSync, readdirSync, readFileSync } from 'node:fs';\nimport { cp, mkdir, writeFile } from 'node:fs/promises';\nimport { createRequire } from 'node:module';\nimport { basename, dirname, join, resolve } from 'node:path';\n\nimport { CommandError } from '../report';\nimport type { Template } from './template';\nimport { projectFiles, TEMPLATE_FILES } from './template';\n\nexport interface CreateOptions {\n name: string;\n dir: string;\n template?: Template;\n}\n\nexport interface CreateResult {\n readonly root: string;\n readonly workspace: boolean;\n}\n\ninterface PackageJson {\n name: string;\n version: string;\n}\n\nexport async function create({\n name,\n dir,\n template = 'starter',\n}: CreateOptions): Promise<CreateResult> {\n const root = resolve(dir);\n if (existsSync(root) && readdirSync(root).length > 0) {\n throw new CommandError(`${dir} already exists and is not empty`);\n }\n const packageName = toPackageName(name);\n if (!packageName) {\n throw new CommandError(`\"${name}\" needs at least one letter or digit`);\n }\n\n const workspace = isInDotWorkspace(root);\n const source = templateDir(template);\n await mkdir(root, { recursive: true });\n for (const path of TEMPLATE_FILES) {\n await cp(join(source, path), join(root, path), { recursive: true });\n }\n const generated = projectFiles({\n name,\n packageName,\n dotVersion: workspace ? 'workspace:*' : `^${cliVersion()}`,\n workspace,\n template,\n });\n for (const [path, content] of Object.entries(generated)) {\n await writeFile(join(root, path), content);\n }\n return { root, workspace };\n}\n\nexport function toPackageName(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n}\n\nexport function toDisplayName(dir: string): string {\n return basename(resolve(dir))\n .split(/[-_\\s]+/)\n .filter(Boolean)\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(' ');\n}\n\n// True when the nearest pnpm workspace above `dir` is the Dot repository.\nexport function isInDotWorkspace(dir: string): boolean {\n let current = dirname(dir);\n while (true) {\n if (existsSync(join(current, 'pnpm-workspace.yaml'))) {\n const runtimeManifest = join(current, 'packages/runtime/package.json');\n if (!existsSync(runtimeManifest)) {\n return false;\n }\n return readPackageJson(runtimeManifest).name === '@monimesl/dot-runtime';\n }\n const parent = dirname(current);\n if (parent === current) {\n return false;\n }\n current = parent;\n }\n}\n\n// The template's example: read from the repository, or from the copy the published CLI ships.\nfunction templateDir(template: Template): string {\n const example = join(cliRoot(), '../../examples', template);\n return existsSync(example) ? example : join(cliRoot(), 'dist/templates', template);\n}\n\nfunction cliVersion(): string {\n return readPackageJson(join(cliRoot(), 'package.json')).version;\n}\n\nfunction readPackageJson(path: string): PackageJson {\n return JSON.parse(readFileSync(path, 'utf8')) as PackageJson;\n}\n\nfunction cliRoot(): string {\n return dirname(createRequire(import.meta.url).resolve('@monimesl/dot-cli/package.json'));\n}\n","import { existsSync } from 'node:fs';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname, join, resolve } from 'node:path';\n\nimport { isPageId } from '@monimesl/dot-core';\nimport type { DotModuleIR } from '@monimesl/dot-script-parser';\nimport { parseDotModule } from '@monimesl/dot-script-parser';\n\nimport { CommandError } from '../report';\nimport { PAGE_FILES } from './template';\n\nexport interface CreatePageResult {\n // The page's folder, `<root>/pages/<path>`.\n readonly dir: string;\n readonly files: readonly string[];\n // How the page got into `defineDot({ pages })` in dot.ts: `manual` when dot.ts couldn't be\n // changed, so the page still needs listing.\n readonly listing: 'added' | 'already' | 'manual';\n}\n\n/**\n * Adds a page to the dot around `cwd`: its folder under pages/, with its three files, listed last in\n * dot.ts.\n */\nexport async function createPage(path: string, cwd: string): Promise<CreatePageResult> {\n if (!isPageId(path)) {\n throw new CommandError(\n `\"${path}\" is not a page path: use letters, digits, \"-\" and \"_\", with \"/\" between folders, such as \"cart/checkout\"`,\n );\n }\n const root = findDotRoot(resolve(cwd));\n if (root === undefined) {\n throw new CommandError('There is no dot.json here or in a folder above: run it inside a dot');\n }\n const dir = join(root, 'pages', path);\n if (existsSync(dir)) {\n throw new CommandError(`pages/${path} already exists`);\n }\n await mkdir(dir, { recursive: true });\n const files: string[] = [];\n for (const [name, content] of Object.entries(PAGE_FILES)) {\n const file = join(dir, name);\n await writeFile(file, content);\n files.push(file);\n }\n return { dir, files, listing: await listPage(join(root, 'dot.ts'), path) };\n}\n\nasync function listPage(dotFile: string, path: string): Promise<CreatePageResult['listing']> {\n if (!existsSync(dotFile)) {\n return 'manual';\n }\n const source = await readFile(dotFile, 'utf8');\n let dot: DotModuleIR;\n try {\n dot = parseDotModule(source, dotFile);\n } catch {\n return 'manual';\n }\n if (dot.pages.includes(path)) {\n return 'already';\n }\n const updated = withPage(source, dot.lastPage, path);\n if (!listsPage(updated, dotFile, path)) {\n return 'manual';\n }\n await writeFile(dotFile, updated);\n return 'added';\n}\n\n// `source` with `path` after its last page, written as the list is: on a line of its own when each\n// page has one, and in the same quotes.\nfunction withPage(source: string, lastPage: DotModuleIR['lastPage'], path: string): string {\n const quote = source[lastPage.start] === '\"' ? '\"' : \"'\";\n const page = `${quote}${path}${quote}`;\n const lineStart = source.lastIndexOf('\\n', lastPage.start - 1) + 1;\n const indent = source.slice(lineStart, lastPage.start);\n if (!/^\\s*$/.test(indent)) {\n return `${source.slice(0, lastPage.end)}, ${page}${source.slice(lastPage.end)}`;\n }\n const trailingComma = /^\\s*,/.exec(source.slice(lastPage.end));\n if (trailingComma) {\n const end = lastPage.end + trailingComma[0].length;\n return `${source.slice(0, end)}\\n${indent}${page},${source.slice(end)}`;\n }\n return `${source.slice(0, lastPage.end)},\\n${indent}${page}${source.slice(lastPage.end)}`;\n}\n\nfunction listsPage(source: string, dotFile: string, path: string): boolean {\n try {\n return parseDotModule(source, dotFile).pages.includes(path);\n } catch {\n return false;\n }\n}\n\nfunction findDotRoot(dir: string): string | undefined {\n while (true) {\n if (existsSync(join(dir, 'dot.json'))) {\n return dir;\n }\n const parent = dirname(dir);\n if (parent === dir) {\n return undefined;\n }\n dir = parent;\n }\n}\n","import { spawnSync } from 'node:child_process';\n\nexport const PACKAGE_MANAGERS = ['npm', 'pnpm', 'yarn', 'bun'] as const;\nexport type PackageManager = (typeof PACKAGE_MANAGERS)[number];\n\nexport function isPackageManager(name: string): name is PackageManager {\n return (PACKAGE_MANAGERS as readonly string[]).includes(name);\n}\n\nexport function launchedBy(userAgent = process.env.npm_config_user_agent ?? ''): PackageManager {\n return PACKAGE_MANAGERS.find((name) => userAgent.startsWith(`${name}/`)) ?? 'npm';\n}\n\nexport function installedManagers(\n isInstalled: (name: PackageManager) => boolean = onPath,\n): PackageManager[] {\n return PACKAGE_MANAGERS.filter(isInstalled);\n}\n\nfunction onPath(name: PackageManager): boolean {\n return (\n spawnSync(name, ['--version'], { stdio: 'ignore', shell: process.platform === 'win32' })\n .status === 0\n );\n}\n","import { createPrivateKey, createPublicKey, sign } from 'node:crypto';\nimport type { KeyObject } from 'node:crypto';\nimport { readFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\n\n// The Ed25519 key this computer signs its dotdev links with. The developer adds its public key to\n// their account, and Monime checks a link's signature before Dash opens it.\nexport interface DeveloperKey {\n // The raw 32-byte public key, base64: what the developer adds to their account.\n readonly publicKey: string;\n // The lowercase hex of the public key's first 8 bytes, which a link names its key by.\n readonly keyId: string;\n // A base64 signature.\n sign(bytes: Buffer): string;\n}\n\nexport function defaultDeveloperKeyPath(): string {\n return process.env.DOT_DEVELOPER_KEY ?? join(homedir(), '.dot', 'developer-key.pem');\n}\n\n// How to make the key, which dotctl reads but leaves to the developer to create.\nexport function missingDeveloperKeyHint(path = defaultDeveloperKeyPath()): string {\n return (\n ` This computer has no developer key in ${path}, and Dash opens only signed links.\\n` +\n \" Make one with OpenSSL 3 (macOS's own openssl can't; brew install openssl):\\n\\n\" +\n ` mkdir -p ${dirname(path)} && openssl genpkey -algorithm ed25519 -out ${path}\\n\\n` +\n ' Then add the public key `dotctl keys show` prints to your Monime account.'\n );\n}\n\n// Returns undefined when this computer has no key yet.\nexport async function loadDeveloperKey(\n path = defaultDeveloperKeyPath(),\n): Promise<DeveloperKey | undefined> {\n let pem: string;\n try {\n pem = await readFile(path, 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return undefined;\n }\n throw error;\n }\n const privateKey = createPrivateKey(pem);\n if (privateKey.asymmetricKeyType !== 'ed25519') {\n throw new Error(`${path} is not an Ed25519 key`);\n }\n return toDeveloperKey(privateKey);\n}\n\nfunction toDeveloperKey(privateKey: KeyObject): DeveloperKey {\n const raw = Buffer.from(createPublicKey(privateKey).export({ format: 'jwk' }).x!, 'base64url');\n return {\n publicKey: raw.toString('base64'),\n keyId: raw.subarray(0, 8).toString('hex'),\n sign: (bytes) => sign(null, bytes, privateKey).toString('base64'),\n };\n}\n","import { createHash, createHmac, randomBytes, timingSafeEqual } from 'node:crypto';\nimport { readdir, readFile } from 'node:fs/promises';\nimport type { IncomingMessage, ServerResponse } from 'node:http';\nimport { createServer } from 'node:http';\nimport type { AddressInfo } from 'node:net';\nimport { networkInterfaces } from 'node:os';\nimport { join, relative, sep } from 'node:path';\n\nimport type { DeveloperKey } from './developer-key';\n\nexport interface DeviceServerOptions {\n // Where devices reach the server; the computer's local network address unless set.\n address?: string | undefined;\n // A free port unless set: the link carries it.\n port?: number | undefined;\n // Each line a device sends, such as its dot's console output.\n onLog?: (line: string, from: string) => void;\n onConnect?: (from: string) => void;\n // Signs the link, for hosts such as Dash that open only a known developer's links.\n developerKey?: DeveloperKey | undefined;\n}\n\n// Serves a dot's builds to the example host apps on devices, which follow them and reload.\nexport interface DeviceServer {\n // What a device opens, `dotdev://connect?server=…&secret=…`, as short as it can be for a small QR\n // code. A signed link's signature is at `GET /dev/link`.\n readonly link: string;\n // Serves the build output in `dir` as the newest build. A `hot` build changed only pages, so\n // devices can run it in place, keeping each open page's state.\n publish(dir: string, options?: { hot?: boolean }): Promise<void>;\n // Tells devices the newest build failed; they keep the last good one.\n fail(message: string): void;\n close(): Promise<void>;\n}\n\ninterface Build {\n readonly files: ReadonlyMap<string, { readonly bytes: Buffer; readonly hash: string }>;\n}\n\nexport const DEVICE_SCHEME = 'dotdev';\n\n// Devices ask again after this, which keeps a waiting request from outliving a quiet network.\nconst WAIT_MS = 25_000;\nconst MAX_LOG_BYTES = 1024 * 1024;\n// How long a signed link opens, so a photo of an old QR code is no use for long.\nconst LINK_LIFETIME_SECONDS = 12 * 60 * 60;\n\nexport const BUILD_MAC_HEADER = 'dot-build-mac';\n\n// What a signed link's `sig` signs, a line each.\nexport function signedLinkMessage(\n server: string,\n secret: string,\n expires: number,\n keyId: string,\n): string {\n return ['dotdev-connect', server, secret, String(expires), keyId].join('\\n');\n}\n\n// What a device sends as its bearer token, made from the link's secret so the link needn't carry it.\n// Seeing it on the network gives away nothing of the secret.\nexport function deviceToken(secret: string): string {\n return createHmac('sha256', Buffer.from(secret, 'base64url'))\n .update('dotdev-token', 'utf8')\n .digest('base64url');\n}\n\n// A signed link's signature, which `GET /dev/link` serves: what isn't secret stays out of the QR code.\nexport interface LinkSignature {\n readonly expires: number;\n readonly keyId: string;\n // Base64.\n readonly sig: string;\n}\n\n// The HMAC-SHA256 of a build listing, keyed with the link's secret: a device takes only listings\n// from whoever has the link, and each file has to match its hash in the listing.\nexport function buildMac(secret: string, body: string): string {\n return createHmac('sha256', Buffer.from(secret, 'base64url'))\n .update(`dotdev-build\\n${body}`, 'utf8')\n .digest('base64url');\n}\n\nexport async function startDeviceServer({\n address = localNetworkAddress(),\n port = 0,\n onLog = () => {},\n onConnect = () => {},\n developerKey,\n}: DeviceServerOptions = {}): Promise<DeviceServer> {\n if (address === undefined) {\n throw new Error('This computer has no local network address for devices to reach it on');\n }\n // It never goes over the network: only the link carries it.\n const secret = randomBytes(16).toString('base64url');\n const token = deviceToken(secret);\n let signature: LinkSignature | null = null;\n let build: Build = { files: new Map() };\n let generation = 0;\n // The oldest generation from which only hot builds lead to the newest: a device on it, or on a\n // later one, can take the newest in place.\n let hotSince = 0;\n let error: string | null = null;\n const waiting = new Set<() => void>();\n const connected = new Set<string>();\n\n const announce = () => {\n generation += 1;\n for (const wake of waiting) {\n wake();\n }\n };\n\n const server = createServer((request, response) => {\n const from = request.socket.remoteAddress ?? 'a device';\n // Before the token: a device needs the signature to check the link before it uses it, and the\n // signature is no secret.\n if (request.method === 'GET' && request.url === '/dev/link') {\n if (signature) {\n sendJson(response, signature);\n } else {\n response.writeHead(404).end();\n }\n return;\n }\n if (!isAuthorized(request, token)) {\n response.writeHead(401).end();\n return;\n }\n const url = new URL(request.url ?? '/', 'http://device');\n const path = url.pathname;\n if (request.method === 'GET' && path === '/dev/build') {\n if (!connected.has(from)) {\n connected.add(from);\n onConnect(from);\n }\n const files = Object.fromEntries([...build.files].map(([name, file]) => [name, file.hash]));\n const body = JSON.stringify({ generation, hotSince, files, error });\n response\n .writeHead(200, {\n 'content-type': 'application/json',\n [BUILD_MAC_HEADER]: buildMac(secret, body),\n })\n .end(body);\n } else if (request.method === 'GET' && path.startsWith('/dev/files/')) {\n if (url.searchParams.get('generation') !== String(generation)) {\n response.writeHead(409).end();\n return;\n }\n const name = path.slice('/dev/files/'.length).split('/').map(decodeURIComponent).join('/');\n const file = build.files.get(name);\n if (!file) {\n response.writeHead(404).end();\n return;\n }\n response.writeHead(200, { 'content-type': 'application/octet-stream' }).end(file.bytes);\n } else if (request.method === 'GET' && path === '/dev/changes') {\n waitForChange(response, Number(url.searchParams.get('after')));\n } else if (request.method === 'POST' && path === '/dev/log') {\n readText(request)\n .then((text) => {\n for (const line of text.split('\\n')) {\n onLog(line, from);\n }\n response.writeHead(204).end();\n })\n .catch(() => response.writeHead(413).end());\n } else {\n response.writeHead(404).end();\n }\n });\n\n const waitForChange = (response: ServerResponse, after: number) => {\n if (generation > after) {\n sendJson(response, { generation });\n return;\n }\n const wake = () => {\n clearTimeout(timer);\n waiting.delete(wake);\n sendJson(response, { generation });\n };\n const timer = setTimeout(() => {\n waiting.delete(wake);\n response.writeHead(204).end();\n }, WAIT_MS);\n waiting.add(wake);\n response.on('close', () => {\n clearTimeout(timer);\n waiting.delete(wake);\n });\n };\n\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject);\n server.listen(port, address, () => resolve());\n });\n const { port: listening } = server.address() as AddressInfo;\n const origin = `http://${address}:${listening}`;\n if (developerKey) {\n const expires = Math.floor(Date.now() / 1000) + LINK_LIFETIME_SECONDS;\n const keyId = developerKey.keyId;\n const message = signedLinkMessage(origin, secret, expires, keyId);\n signature = { expires, keyId, sig: developerKey.sign(Buffer.from(message, 'utf8')) };\n }\n\n return {\n link: `${DEVICE_SCHEME}://connect?server=${encodeURIComponent(origin)}&secret=${secret}`,\n async publish(dir, { hot = false } = {}) {\n build = await readBuild(dir);\n error = null;\n announce();\n if (!hot) {\n hotSince = generation;\n }\n },\n fail(message) {\n error = message;\n announce();\n },\n close() {\n for (const wake of waiting) {\n wake();\n }\n return new Promise((resolve) => {\n server.close(() => resolve());\n server.closeAllConnections();\n });\n },\n };\n}\n\n// The first IPv4 address on a network other than this computer's own.\nexport function localNetworkAddress(): string | undefined {\n for (const addresses of Object.values(networkInterfaces())) {\n for (const candidate of addresses ?? []) {\n if (candidate.family === 'IPv4' && !candidate.internal) {\n return candidate.address;\n }\n }\n }\n return undefined;\n}\n\n// A copy of the build output, so a device downloading it never mixes two builds. Source maps stay\n// on the computer.\nasync function readBuild(dir: string): Promise<Build> {\n const files = new Map<string, { bytes: Buffer; hash: string }>();\n const entries = await readdir(dir, { recursive: true, withFileTypes: true });\n for (const entry of entries) {\n // Source maps stay on the computer, and archives are for `dotctl build`.\n if (!entry.isFile() || entry.name.endsWith('.map') || entry.name.endsWith('.zip')) {\n continue;\n }\n const path = join(entry.parentPath, entry.name);\n const bytes = await readFile(path);\n const hash = createHash('sha256').update(bytes).digest('base64url');\n files.set(relative(dir, path).split(sep).join('/'), { bytes, hash });\n }\n return { files };\n}\n\nfunction isAuthorized(request: IncomingMessage, token: string): boolean {\n const given = Buffer.from(request.headers.authorization ?? '');\n const expected = Buffer.from(`Bearer ${token}`);\n return given.length === expected.length && timingSafeEqual(given, expected);\n}\n\nfunction sendJson(response: ServerResponse, body: unknown): void {\n response.writeHead(200, { 'content-type': 'application/json' }).end(JSON.stringify(body));\n}\n\nasync function readText(request: IncomingMessage): Promise<string> {\n const chunks: Buffer[] = [];\n let size = 0;\n for await (const chunk of request) {\n size += (chunk as Buffer).length;\n if (size > MAX_LOG_BYTES) {\n throw new Error('Too much log at once');\n }\n chunks.push(chunk as Buffer);\n }\n return Buffer.concat(chunks).toString('utf8');\n}\n","import { watch } from 'node:fs';\nimport { join, relative, resolve, sep } from 'node:path';\n\nimport type { BuildOptions } from '@monimesl/dot-compiler';\nimport { build, installedRuntime, loadProject, PAGES_DIR } from '@monimesl/dot-compiler';\nimport { startSimulator } from '@monimesl/dot-simulator';\nimport { renderUnicodeCompact } from 'uqr';\n\nimport { loadDeveloperKey, missingDeveloperKeyHint } from './device/developer-key';\nimport type { DeviceServer } from './device/server';\nimport { startDeviceServer } from './device/server';\nimport { describeError, printBuildTime, printWarnings } from './report';\n\nexport interface DevOptions extends BuildOptions {\n port?: number | undefined;\n // Also serves the dot to the example host apps on devices, through a link and a QR code.\n device?: boolean | undefined;\n}\n\n// Editors save a file in several writes; waiting this long turns them into one rebuild.\nconst CHANGE_DEBOUNCE_MS = 50;\n\nexport async function dev({ port, device, ...options }: DevOptions): Promise<() => Promise<void>> {\n const project = await loadProject(options.root);\n const outDir = resolve(project.root, options.outDir ?? 'dist');\n const buildOptions: BuildOptions = { mode: 'development', ...options, outDir };\n\n // Returns the error to show, with paths relative to the project.\n const buildApp = async (): Promise<string | null> => {\n const started = performance.now();\n try {\n printWarnings(await build(buildOptions));\n printBuildTime(started);\n return null;\n } catch (error) {\n const message = describeError(error).replaceAll(project.root + sep, '');\n console.error(`error: ${message}`);\n return message;\n }\n };\n\n const firstError = await buildApp();\n const simulator = await startSimulator({\n dotDir: outDir,\n reactHostDir: installedRuntime(project.root).react.dir,\n name: project.dotJson.name,\n port,\n storageDir: join(project.root, '.dot', 'files'),\n });\n if (firstError) {\n simulator.error(firstError);\n }\n console.log(`\\n Simulator running at: ${simulator.url}\\n`);\n\n let devices: DeviceServer | undefined;\n if (device) {\n const developerKey = await loadDeveloperKey();\n devices = await startDeviceServer({\n developerKey,\n onConnect: (from) => console.log(`A device connected from ${from}`),\n onLog: (line) => console.log(`device ${line}`),\n });\n if (firstError) {\n devices.fail(firstError);\n } else {\n await devices.publish(outDir);\n }\n printDeviceLink(devices.link);\n if (developerKey) {\n console.log(` Signed with developer key ${developerKey.keyId}\\n`);\n } else {\n console.log(`${missingDeveloperKeyHint()}\\n`);\n }\n }\n\n // What changed since the last build that worked, to tell whether the new one can run in place.\n const changed = new Set<string>();\n const pagesDir = join(project.root, PAGES_DIR) + sep;\n const rebuilds = new RebuildQueue(async () => {\n // Changes that come in while it builds count toward the next build.\n const building = [...changed];\n const error = await buildApp();\n if (error) {\n simulator.error(error);\n devices?.fail(error);\n return;\n }\n for (const path of building) {\n changed.delete(path);\n }\n // Pages run again in place; anything else, such as dot.ts, .env or statics/, starts the dot\n // again on the pages it had open.\n const hot = building.every((path) => path.startsWith(pagesDir));\n if (hot) {\n simulator.hotReload();\n } else {\n simulator.reload();\n }\n await devices?.publish(outDir, { hot });\n });\n\n let debounceTimer: NodeJS.Timeout | undefined;\n const watcher = watch(project.root, { recursive: true }, (_event, file) => {\n if (!file || isIgnored(file)) {\n return;\n }\n const path = resolve(project.root, file);\n if (path === outDir || path.startsWith(outDir + sep)) {\n return;\n }\n changed.add(path);\n clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n console.log(`Changed ${relative(project.root, path)}`);\n rebuilds.request();\n }, CHANGE_DEBOUNCE_MS);\n });\n\n return async () => {\n clearTimeout(debounceTimer);\n watcher.close();\n await Promise.all([simulator.close(), devices?.close()]);\n };\n}\n\nfunction printDeviceLink(link: string): void {\n console.log(' Scan this in Dash or a Dot example app on a phone:\\n');\n console.log(renderUnicodeCompact(link, { border: 1 }).replace(/^/gm, ' '));\n console.log(`\\n Or open the link on a device, an emulator or a simulator:\\n\\n ${link}\\n`);\n console.log(` Android emulator: adb shell am start -a android.intent.action.VIEW -d '${link}'`);\n console.log(` iOS Simulator: xcrun simctl openurl booted '${link}'\\n`);\n}\n\n// Runs one rebuild at a time; changes that arrive mid-build trigger exactly one more once it ends.\nclass RebuildQueue {\n readonly #rebuild: () => Promise<void>;\n #building = false;\n #changedWhileBuilding = false;\n\n constructor(rebuild: () => Promise<void>) {\n this.#rebuild = rebuild;\n }\n\n request(): void {\n if (this.#building) {\n this.#changedWhileBuilding = true;\n return;\n }\n void this.#build();\n }\n\n async #build(): Promise<void> {\n this.#building = true;\n this.#changedWhileBuilding = false;\n try {\n await this.#rebuild();\n } finally {\n this.#building = false;\n }\n if (this.#changedWhileBuilding) {\n this.request();\n }\n }\n}\n\n// Dependencies and hidden folders such as .git or .dot, but not .env files, which the build reads.\nfunction isIgnored(file: string): boolean {\n return file.split(/[\\\\/]/).some((segment) => {\n if (segment === '.env' || segment.startsWith('.env.')) {\n return false;\n }\n return segment === 'node_modules' || segment.startsWith('.');\n });\n}\n","import { spawn } from 'node:child_process';\nimport { relative } from 'node:path';\nimport { createInterface } from 'node:readline/promises';\nimport { parseArgs } from 'node:util';\n\nimport type { BuildOptions } from '@monimesl/dot-compiler';\nimport { build } from '@monimesl/dot-compiler';\nimport { DEFAULT_PORT } from '@monimesl/dot-simulator';\n\nimport { create, toDisplayName, toPackageName } from './create/create';\nimport { createPage } from './create/page';\nimport type { PackageManager } from './create/package-manager';\nimport type { Template } from './create/template';\nimport { isTemplate, TEMPLATES } from './create/template';\nimport {\n installedManagers,\n isPackageManager,\n launchedBy,\n PACKAGE_MANAGERS,\n} from './create/package-manager';\nimport { dev } from './dev';\nimport {\n defaultDeveloperKeyPath,\n loadDeveloperKey,\n missingDeveloperKeyHint,\n} from './device/developer-key';\nimport { describeError, printBuildTime, printWarnings } from './report';\n\nconst USAGE = `Usage:\n dotctl create [dir] [--name <name>] [--template <starter|showcase>]\n [--pm <npm|pnpm|yarn|bun>]\n Create a dot (asks for its name and template):\n starter is one page to build on, showcase a page\n for each feature\n dotctl create page <path> Add a page to the dot you are in: pages/<path>/ with\n view.dml, state.ts and controller.ts\n dotctl build [root] [options] Build the minified bundles the host app runs\n dotctl dev [root] [options] Run in the browser simulator, rebuilding on change\n dotctl keys show Show the public key and key ID of this computer's\n developer key, which signs the links \\`dev --device\\`\n shows, for Dash to open\n dotctl lsp [--stdio] Serve .dml files to an editor over the Language Server\n Protocol, on stdin and stdout\n\nOptions:\n --out-dir <dir> Output directory (default: <root>/dist)\n --conditions <list> Extra export conditions, comma separated\n --port <port> Simulator port for \\`dev\\`; the next free one is used if taken\n (default: ${DEFAULT_PORT})\n --device With \\`dev\\`, also serve the dot to the Dot example apps on phones on\n the local network, through a link and QR code; they reload on each build\n -h, --help Show this message`;\n\nasync function main(argv: string[]): Promise<number> {\n const { values, positionals } = parseArgs({\n args: argv,\n allowPositionals: true,\n options: {\n 'out-dir': { type: 'string' },\n conditions: { type: 'string' },\n port: { type: 'string' },\n device: { type: 'boolean' },\n name: { type: 'string' },\n template: { type: 'string' },\n pm: { type: 'string' },\n help: { type: 'boolean', short: 'h' },\n // Editors pass it to say the server talks over stdin and stdout, which is the only way it does.\n stdio: { type: 'boolean' },\n },\n });\n\n const [command, target, pagePath] = positionals;\n if (command === 'lsp' && !values.help) {\n // Loaded only here, so the other commands start without it.\n const { serveStdio } = await import('./lsp/server');\n serveStdio();\n // The connection exits the process when the editor says so.\n return new Promise<number>(() => {});\n }\n if (command === 'keys' && !values.help) {\n return keys(target);\n }\n if (command === 'create' && target === 'page' && !values.help) {\n return addPage(pagePath);\n }\n if (command === 'create' && !values.help) {\n return createApp(target, values.name, values.template, values.pm);\n }\n if (values.help || (command !== 'build' && command !== 'dev')) {\n console.log(USAGE);\n return values.help ? 0 : 1;\n }\n const port = values.port === undefined ? undefined : Number(values.port);\n if (port !== undefined && !Number.isInteger(port)) {\n console.error(`Invalid --port \"${values.port}\"`);\n return 1;\n }\n\n const options: BuildOptions = { root: target ?? '.' };\n if (values['out-dir'] !== undefined) {\n options.outDir = values['out-dir'];\n }\n if (values.conditions !== undefined) {\n options.conditions = values.conditions.split(',');\n }\n\n if (command === 'dev') {\n const stop = await dev({ ...options, port, device: values.device });\n await new Promise((resolve) => process.once('SIGINT', resolve));\n await stop();\n return 0;\n }\n\n const started = performance.now();\n const result = await build(options);\n for (const file of result.files.filter((file) => !file.endsWith('.map'))) {\n console.log(` ${relative(process.cwd(), file)}`);\n }\n printWarnings(result);\n printBuildTime(started);\n return 0;\n}\n\nasync function keys(action: string | undefined): Promise<number> {\n if (action !== 'show') {\n console.error('dotctl keys show');\n return 1;\n }\n const path = defaultDeveloperKeyPath();\n const key = await loadDeveloperKey(path);\n if (!key) {\n console.error(missingDeveloperKeyHint(path));\n return 1;\n }\n console.log(`\\n Public key: ${key.publicKey}\\n Key ID: ${key.keyId}\\n`);\n return 0;\n}\n\nasync function addPage(path: string | undefined): Promise<number> {\n if (path === undefined) {\n console.error(\n 'Which page? dotctl create page <path>, such as dotctl create page cart/checkout',\n );\n return 1;\n }\n if (path.startsWith('/')) {\n path = path.substring(1);\n }\n if (path.endsWith('/')) {\n path = path.substring(0, path.length - 1);\n }\n const { files, listing } = await createPage(path, process.cwd());\n for (const file of files) {\n console.log(` ${relative(process.cwd(), file)}`);\n }\n if (listing === 'added') {\n console.log(`\\nListed '${path}' in dot.ts.`);\n } else if (listing === 'manual') {\n console.log(`\\nList it in dot.ts to build it: defineDot({ pages: [..., '${path}'] })`);\n }\n return 0;\n}\n\nasync function createApp(\n dir: string | undefined,\n name: string | undefined,\n templateFlag: string | undefined,\n packageManagerFlag: string | undefined,\n): Promise<number> {\n if (templateFlag !== undefined && !isTemplate(templateFlag)) {\n console.error(`--template must be one of ${TEMPLATES.join(', ')}`);\n return 1;\n }\n if (packageManagerFlag !== undefined && !isPackageManager(packageManagerFlag)) {\n console.error(`--pm must be one of ${PACKAGE_MANAGERS.join(', ')}`);\n return 1;\n }\n name ??= await ask('Dot name:', dir ? toDisplayName(dir) : 'Dot');\n if (!name) {\n console.error('A dot needs a name: dotctl create [dir] --name \"My Dot\"');\n return 1;\n }\n const template = templateFlag ?? (await chooseTemplate());\n const { root, workspace } = await create({\n name,\n dir: dir ?? toPackageName(name),\n template,\n });\n const path = relative(process.cwd(), root) || '.';\n console.log(`\\nCreated ${name} in ${path}`);\n if (workspace) {\n console.log('It is inside the Dot repository, so it uses the workspace packages.');\n }\n\n // A command cannot change its shell's directory, so it offers to carry on inside the new dot instead.\n let packageManager: PackageManager = workspace ? 'pnpm' : (packageManagerFlag ?? launchedBy());\n if (await confirm('\\nInstall and start it now?')) {\n if (!workspace && !packageManagerFlag) {\n packageManager = await choosePackageManager(packageManager);\n }\n const installed = await run(packageManager, ['install'], root);\n if (installed === 0) {\n return run(packageManager, ['run', 'dev'], root);\n }\n console.error(`\\n${packageManager} install failed.`);\n }\n console.log(\n `\\nNext:\\n\\n cd ${path}\\n ${packageManager} install\\n ${packageManager} run dev\\n`,\n );\n return 0;\n}\n\nfunction run(command: string, args: string[], cwd: string): Promise<number> {\n return new Promise((resolvePromise, reject) => {\n const child = spawn(command, args, {\n cwd,\n stdio: 'inherit',\n shell: process.platform === 'win32',\n });\n child.on('error', reject);\n child.on('exit', (code) => resolvePromise(code ?? 1));\n });\n}\n\nasync function confirm(question: string): Promise<boolean> {\n if (!process.stdin.isTTY) {\n return false;\n }\n const answer = await readAnswer(`${question} (Y/n)`, 'y');\n return answer !== undefined && answer.toLowerCase().startsWith('y');\n}\n\n// Shows the fallback after the question, as the answer an empty reply gives.\nasync function ask(question: string, fallback?: string): Promise<string | undefined> {\n const hint = fallback ? ` (${fallback})` : '';\n return readAnswer(`${question}${hint}`, fallback);\n}\n\nasync function readAnswer(prompt: string, fallback?: string): Promise<string | undefined> {\n if (!process.stdin.isTTY) {\n return fallback;\n }\n const readline = createInterface({ input: process.stdin, output: process.stdout });\n try {\n const answer = await readline.question(`${prompt} `);\n return answer.trim() || fallback;\n } catch (error) {\n // Ctrl+C or Ctrl+D\n if ((error as Error).name === 'AbortError') {\n return undefined;\n }\n throw error;\n } finally {\n readline.close();\n }\n}\n\nasync function chooseTemplate(): Promise<Template> {\n if (!process.stdin.isTTY) {\n return 'starter';\n }\n console.log(\n '\\nTemplates:\\n starter one page to build on\\n showcase a page for each feature, to read',\n );\n return chooseOne('Template?', TEMPLATES, 'starter');\n}\n\nasync function choosePackageManager(suggested: PackageManager): Promise<PackageManager> {\n const installed = installedManagers();\n if (installed.length < 2) {\n return installed[0] ?? suggested;\n }\n const fallback = installed.includes(suggested) ? suggested : installed[0]!;\n return chooseOne('Package manager?', installed, fallback);\n}\n\n// Asks until the answer is one of `options`; an empty answer, or none, is `fallback`.\nasync function chooseOne<Option extends string>(\n question: string,\n options: readonly Option[],\n fallback: Option,\n): Promise<Option> {\n while (true) {\n const answer = await ask(`${question} ${options.join(' / ')}`, fallback);\n if (answer === undefined) {\n return fallback;\n }\n const chosen = options.find((option) => option === answer);\n if (chosen) {\n return chosen;\n }\n console.log(`Choose one of ${options.join(', ')}.`);\n }\n}\n\nmain(process.argv.slice(2)).then(\n (code) => process.exit(code),\n (error: unknown) => {\n console.error(`error: ${describeError(error)}`);\n process.exit(1);\n },\n);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.js","names":[],"sources":["../src/report.ts","../src/create/template.ts","../src/create/create.ts","../src/dot-root.ts","../src/create/page.ts","../src/create/package-manager.ts","../src/device/developer-key.ts","../src/device/server.ts","../src/simulator.ts","../src/dev.ts","../src/preview.ts","../src/prompts.ts","../src/cli.ts"],"sourcesContent":["import { DmlParseError } from '@monimesl/dot-dml-parser';\nimport type { BuildResult } from '@monimesl/dot-compiler';\nimport { CompileError } from '@monimesl/dot-compiler';\nimport { ScriptParseError } from '@monimesl/dot-script-parser';\n\n/** A mistake in how a command was used, which its message alone explains. */\nexport class CommandError extends Error {\n override readonly name = 'CommandError';\n}\n\nexport function describeError(error: unknown): string {\n const known =\n error instanceof CommandError ||\n error instanceof CompileError ||\n error instanceof DmlParseError ||\n error instanceof ScriptParseError;\n if (known) {\n return error.message;\n }\n if ((error as { code?: string }).code?.startsWith('ERR_PARSE_ARGS')) {\n // Node's message goes on to explain `--` for positional arguments, which is noise here.\n const [firstSentence] = (error as Error).message.split('. ');\n return `${firstSentence}. See dotctl --help.`;\n }\n return error instanceof Error ? (error.stack ?? error.message) : String(error);\n}\n\nexport function printWarnings({ warnings }: BuildResult): void {\n for (const warning of warnings) {\n console.warn(`warning: ${warning.message}`);\n }\n}\n\nexport function printBuildTime(startedAt: number): void {\n console.log(`Built in ${Math.round(performance.now() - startedAt)}ms`);\n}\n","export const TEMPLATES = ['starter', 'showcase'] as const;\n\nexport type Template = (typeof TEMPLATES)[number];\n\n// What each template starts a dot with.\nexport const TEMPLATE_HINTS: Record<Template, string> = {\n starter: 'one page to build on',\n showcase: 'a page for each feature, to read',\n};\n\nexport function isTemplate(name: string): name is Template {\n return (TEMPLATES as readonly string[]).includes(name);\n}\n\ninterface TemplateOptions {\n name: string;\n packageName: string;\n dotVersion: string;\n workspace: boolean;\n template: Template;\n}\n\nexport const PAGE_FILES: Readonly<Record<string, string>> = {\n 'view.dml': '<View>\\n\\t<Text>My new page</Text>\\n</View>\\n',\n 'state.ts': 'export default {};\\n',\n 'controller.ts': `import state from './state';\n\nexport default definePage({\n initialState: state,\n});\n`,\n};\n\n// The dot itself is copied from the template's example; these are the files around it.\nexport const TEMPLATE_FILES = ['dot.ts', 'pages', 'statics', '.env'] as const;\n\n// For editors that follow `$schema`, they can check dot.json and complete its fields.\nconst DOT_JSON_SCHEMA = './node_modules/@monimesl/dot-runtime/dot.schema.json';\n\nconst json = (value: unknown) => `${JSON.stringify(value, null, 2)}\\n`;\n\nexport function projectFiles(options: TemplateOptions): Record<string, string> {\n return {\n 'package.json': packageJson(options),\n 'tsconfig.json': tsconfig(options),\n '.gitignore': 'node_modules/\\ndist/\\n# Types generated by dotctl\\n.dot/\\n',\n 'dot.json': dotJson(options),\n 'README.md': readme(options),\n };\n}\n\nfunction packageJson({ packageName, dotVersion, workspace }: TemplateOptions): string {\n const conditions = workspace ? ' --conditions @monimesl/dot-source' : '';\n return json({\n name: packageName,\n private: true,\n type: 'module',\n scripts: {\n dev: `dotctl dev${conditions}`,\n build: `dotctl build${conditions}`,\n preview: `dotctl preview${conditions}`,\n typecheck: 'tsc --noEmit',\n },\n dependencies: { '@monimesl/dot-runtime': dotVersion },\n devDependencies: { '@monimesl/dot-cli': dotVersion, typescript: '^5.9.3' },\n });\n}\n\nfunction tsconfig({ workspace }: TemplateOptions): string {\n const compilerOptions: Record<string, unknown> = {\n target: 'ES2022',\n module: 'ESNext',\n moduleResolution: 'bundler',\n lib: ['ES2022'],\n types: ['@monimesl/dot-runtime/globals'],\n strict: true,\n noEmit: true,\n skipLibCheck: true,\n verbatimModuleSyntax: true,\n };\n if (workspace) {\n compilerOptions.customConditions = ['@monimesl/dot-source'];\n }\n return json({\n compilerOptions,\n include: ['dot.ts', 'pages', '.dot/pages.d.ts', '.dot/env.d.ts'],\n });\n}\n\nfunction dotJson({ name, packageName, template }: TemplateOptions): string {\n const dot: Record<string, unknown> = {\n $schema: DOT_JSON_SCHEMA,\n id: `com.example.${idWord(packageName)}`,\n name,\n version: '0.1.0',\n };\n if (template === 'showcase') {\n // The showcase's network page reaches these.\n dot.network = { hosts: ['itunes.apple.com', '*.mzstatic.com', 'httpbin.org'] };\n }\n return json(dot);\n}\n\nconst TEMPLATE_READMES: Readonly<Record<Template, string>> = {\n starter: `It starts with one page, \\`index\\`: a greeting from \\`.env\\` and a count to tap. Change it,\nand add pages as the dot grows. \\`npx dotctl create my-tour --template showcase\\` makes a dot with a\npage for each feature, to read how each is done.`,\n showcase: `It starts as a showcase: \\`index\\` lists a page for each feature, from layout, text, images and\ninputs to state, navigation, and Dash's alerts, network and files. Read a page to see how it's\ndone, then delete the ones you don't need, from \\`pages/\\` and from \\`dot.ts\\`.`,\n};\n\nfunction readme({ name, template }: TemplateOptions): string {\n return `# ${name}\n\nA dot: a mini app that runs inside Dash.\n\n\\`\\`\\`sh\nnpm install\nnpm run dev # run it in the simulator; it reloads on every change\nnpm run build # build it for Dash\n\\`\\`\\`\n\nEach page is a folder under \\`pages/\\` with three files: \\`view.dml\\` (what it shows),\n\\`state.ts\\` (what it starts with) and \\`controller.ts\\` (how it changes). A page's markup is one\nroot \\`<View>\\` with everything else inside it. \\`npx dotctl create page <path>\\` adds a page, such\nas \\`cart/checkout\\`, with the three files to start from, and lists it in \\`dot.ts\\`. The first page\nthere is where the dot starts.\n\n\\`dot.json\\` describes the dot to Dash. Change its \\`id\\` from \\`com.example.…\\` to a reverse domain\nname you own before you publish: it can't change afterwards. Its \\`network.hosts\\` lists every host\nthe dot sends requests to with \\`dash.network\\`; requests to any other fail. Your editor completes\nthe other fields from the schema it points to.\n\n\\`.env\\` holds public configuration, which the service reads as \\`env.GREETING\\`. Every variable\nthere is built into the dot, so anyone who has it can read them: never put a secret there.\n\\`.env.development\\` and \\`.env.production\\` override it for \\`dev\\` and \\`build\\`.\n\n${TEMPLATE_READMES[template]}\n`;\n}\n\n// A word of the dot's id: the package name, which is already lowercase letters, digits and inner\n// hyphens, cut to the 63 characters a host name allows.\nfunction idWord(packageName: string): string {\n return packageName.slice(0, 63).replace(/-+$/, '');\n}\n","import { existsSync, readdirSync, readFileSync } from 'node:fs';\nimport { cp, mkdir, writeFile } from 'node:fs/promises';\nimport { createRequire } from 'node:module';\nimport { basename, dirname, join, resolve } from 'node:path';\n\nimport { CommandError } from '../report';\nimport type { Template } from './template';\nimport { projectFiles, TEMPLATE_FILES } from './template';\n\ninterface CreateOptions {\n name: string;\n dir: string;\n template?: Template;\n}\n\ninterface CreateResult {\n readonly root: string;\n readonly workspace: boolean;\n}\n\ninterface PackageJson {\n name: string;\n version: string;\n}\n\nexport async function create({\n name,\n dir,\n template = 'starter',\n}: CreateOptions): Promise<CreateResult> {\n const root = resolve(dir);\n if (existsSync(root) && readdirSync(root).length > 0) {\n throw new CommandError(`${dir} already exists and is not empty`);\n }\n const packageName = toPackageName(name);\n if (!packageName) {\n throw new CommandError(`\"${name}\" needs at least one letter or digit`);\n }\n\n const workspace = isInDotWorkspace(root);\n const source = templateDir(template);\n await mkdir(root, { recursive: true });\n for (const path of TEMPLATE_FILES) {\n await cp(join(source, path), join(root, path), { recursive: true });\n }\n const generated = projectFiles({\n name,\n packageName,\n dotVersion: workspace ? 'workspace:*' : `^${cliVersion()}`,\n workspace,\n template,\n });\n for (const [path, content] of Object.entries(generated)) {\n await writeFile(join(root, path), content);\n }\n return { root, workspace };\n}\n\nexport function toPackageName(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n}\n\nexport function toDisplayName(dir: string): string {\n return basename(resolve(dir))\n .split(/[-_\\s]+/)\n .filter(Boolean)\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(' ');\n}\n\n// True when the nearest pnpm workspace above `dir` is the Dot repository.\nexport function isInDotWorkspace(dir: string): boolean {\n let current = dirname(dir);\n while (true) {\n if (existsSync(join(current, 'pnpm-workspace.yaml'))) {\n const runtimeManifest = join(current, 'packages/runtime/package.json');\n if (!existsSync(runtimeManifest)) {\n return false;\n }\n return readPackageJson(runtimeManifest).name === '@monimesl/dot-runtime';\n }\n const parent = dirname(current);\n if (parent === current) {\n return false;\n }\n current = parent;\n }\n}\n\n// The template's example: read from the repository, or from the copy the published CLI ships.\nfunction templateDir(template: Template): string {\n const example = join(cliRoot(), '../../examples', template);\n return existsSync(example) ? example : join(cliRoot(), 'dist/templates', template);\n}\n\nfunction cliVersion(): string {\n return readPackageJson(join(cliRoot(), 'package.json')).version;\n}\n\nfunction readPackageJson(path: string): PackageJson {\n return JSON.parse(readFileSync(path, 'utf8')) as PackageJson;\n}\n\nfunction cliRoot(): string {\n return dirname(createRequire(import.meta.url).resolve('@monimesl/dot-cli/package.json'));\n}\n","import { existsSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\n\n// The nearest folder at or above `dir` with a dot.json.\nexport function findDotRoot(dir: string): string | undefined {\n while (true) {\n if (existsSync(join(dir, 'dot.json'))) {\n return dir;\n }\n const parent = dirname(dir);\n if (parent === dir) {\n return undefined;\n }\n dir = parent;\n }\n}\n","import { existsSync } from 'node:fs';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join, resolve } from 'node:path';\n\nimport { isPageId } from '@monimesl/dot-core';\nimport type { DotModuleIR } from '@monimesl/dot-script-parser';\nimport { parseDotModule } from '@monimesl/dot-script-parser';\n\nimport { findDotRoot } from '../dot-root';\nimport { CommandError } from '../report';\nimport { PAGE_FILES } from './template';\n\ninterface CreatePageResult {\n readonly dir: string;\n readonly files: readonly string[];\n // How the page got into `defineDot({ pages })` in dot.ts: `manual` when dot.ts couldn't be\n // changed, so the page still needs listing.\n readonly listing: 'added' | 'already' | 'manual';\n}\n\n// Adds a page to the dot around `cwd`: its folder under pages/, listed last in dot.ts.\nexport async function createPage(path: string, cwd: string): Promise<CreatePageResult> {\n if (!isPageId(path)) {\n throw new CommandError(\n `\"${path}\" is not a page path: use letters, digits, \"-\" and \"_\", with \"/\" between folders, such as \"cart/checkout\"`,\n );\n }\n const root = findDotRoot(resolve(cwd));\n if (root === undefined) {\n throw new CommandError('There is no dot.json here or in a folder above: run it inside a dot');\n }\n const dir = join(root, 'pages', path);\n if (existsSync(dir)) {\n throw new CommandError(`pages/${path} already exists`);\n }\n await mkdir(dir, { recursive: true });\n const files: string[] = [];\n for (const [name, content] of Object.entries(PAGE_FILES)) {\n const file = join(dir, name);\n await writeFile(file, content);\n files.push(file);\n }\n return { dir, files, listing: await listPage(join(root, 'dot.ts'), path) };\n}\n\nasync function listPage(dotFile: string, path: string): Promise<CreatePageResult['listing']> {\n if (!existsSync(dotFile)) {\n return 'manual';\n }\n const source = await readFile(dotFile, 'utf8');\n let dot: DotModuleIR;\n try {\n dot = parseDotModule(source, dotFile);\n } catch {\n return 'manual';\n }\n if (dot.pages.includes(path)) {\n return 'already';\n }\n const updated = withPage(source, dot.lastPage, path);\n if (!listsPage(updated, dotFile, path)) {\n return 'manual';\n }\n await writeFile(dotFile, updated);\n return 'added';\n}\n\n// `source` with `path` after its last page, written as the list is: on a line of its own when each\n// page has one, and in the same quotes.\nfunction withPage(source: string, lastPage: DotModuleIR['lastPage'], path: string): string {\n const quote = source[lastPage.start] === '\"' ? '\"' : \"'\";\n const page = `${quote}${path}${quote}`;\n const lineStart = source.lastIndexOf('\\n', lastPage.start - 1) + 1;\n const indent = source.slice(lineStart, lastPage.start);\n if (!/^\\s*$/.test(indent)) {\n return `${source.slice(0, lastPage.end)}, ${page}${source.slice(lastPage.end)}`;\n }\n const trailingComma = /^\\s*,/.exec(source.slice(lastPage.end));\n if (trailingComma) {\n const end = lastPage.end + trailingComma[0].length;\n return `${source.slice(0, end)}\\n${indent}${page},${source.slice(end)}`;\n }\n return `${source.slice(0, lastPage.end)},\\n${indent}${page}${source.slice(lastPage.end)}`;\n}\n\nfunction listsPage(source: string, dotFile: string, path: string): boolean {\n try {\n return parseDotModule(source, dotFile).pages.includes(path);\n } catch {\n return false;\n }\n}\n","import { spawnSync } from 'node:child_process';\n\nexport const PACKAGE_MANAGERS = ['npm', 'pnpm', 'yarn', 'bun'] as const;\nexport type PackageManager = (typeof PACKAGE_MANAGERS)[number];\n\nexport function isPackageManager(name: string): name is PackageManager {\n return (PACKAGE_MANAGERS as readonly string[]).includes(name);\n}\n\nexport function launchedBy(userAgent = process.env.npm_config_user_agent ?? ''): PackageManager {\n return PACKAGE_MANAGERS.find((name) => userAgent.startsWith(`${name}/`)) ?? 'npm';\n}\n\nexport function installedManagers(\n isInstalled: (name: PackageManager) => boolean = onPath,\n): PackageManager[] {\n return PACKAGE_MANAGERS.filter(isInstalled);\n}\n\nfunction onPath(name: PackageManager): boolean {\n return (\n spawnSync(name, ['--version'], { stdio: 'ignore', shell: process.platform === 'win32' })\n .status === 0\n );\n}\n","import { createPrivateKey, createPublicKey, sign } from 'node:crypto';\nimport type { KeyObject } from 'node:crypto';\nimport { readFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\n\n// The Ed25519 key this computer signs its dotdev links with. The developer adds its public key to\n// their account, and Monime checks a link's signature before Dash opens it.\nexport interface DeveloperKey {\n // The raw 32-byte public key, base64: what the developer adds to their account.\n readonly publicKey: string;\n // The lowercase hex of the public key's first 8 bytes, which a link names its key by.\n readonly keyId: string;\n // A base64 signature.\n sign(bytes: Buffer): string;\n}\n\nexport function defaultDeveloperKeyPath(): string {\n return process.env.DOT_DEVELOPER_KEY ?? join(homedir(), '.dot', 'developer-key.pem');\n}\n\n// How to make the key, which dotctl reads but leaves to the developer to create.\nexport function missingDeveloperKeyHint(path = defaultDeveloperKeyPath()): string {\n return (\n ` This computer has no developer key in ${path}, and Dash opens only signed links.\\n` +\n \" Make one with OpenSSL 3 (macOS's own openssl can't; brew install openssl):\\n\\n\" +\n ` mkdir -p ${dirname(path)} && openssl genpkey -algorithm ed25519 -out ${path}\\n\\n` +\n ' Then add the public key `dotctl keys show` prints to your Monime account.'\n );\n}\n\n// Returns undefined when this computer has no key yet.\nexport async function loadDeveloperKey(\n path = defaultDeveloperKeyPath(),\n): Promise<DeveloperKey | undefined> {\n let pem: string;\n try {\n pem = await readFile(path, 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return undefined;\n }\n throw error;\n }\n const privateKey = createPrivateKey(pem);\n if (privateKey.asymmetricKeyType !== 'ed25519') {\n throw new Error(`${path} is not an Ed25519 key`);\n }\n return toDeveloperKey(privateKey);\n}\n\nfunction toDeveloperKey(privateKey: KeyObject): DeveloperKey {\n const raw = Buffer.from(createPublicKey(privateKey).export({ format: 'jwk' }).x!, 'base64url');\n return {\n publicKey: raw.toString('base64'),\n keyId: raw.subarray(0, 8).toString('hex'),\n sign: (bytes) => sign(null, bytes, privateKey).toString('base64'),\n };\n}\n","import { createHash, createHmac, randomBytes, timingSafeEqual } from 'node:crypto';\nimport { readdir, readFile } from 'node:fs/promises';\nimport type { IncomingMessage, ServerResponse } from 'node:http';\nimport { createServer } from 'node:http';\nimport type { AddressInfo } from 'node:net';\nimport { networkInterfaces } from 'node:os';\nimport { join, relative, sep } from 'node:path';\n\nimport type { DeveloperKey } from './developer-key';\n\ninterface DeviceServerOptions {\n // Where devices reach the server; the computer's local network address unless set.\n address?: string | undefined;\n // A free port unless set: the link carries it.\n port?: number | undefined;\n // Each line a device sends, such as its dot's console output.\n onLog?: (line: string, from: string) => void;\n onConnect?: (from: string) => void;\n // Signs the link, for hosts such as Dash that open only a known developer's links.\n developerKey?: DeveloperKey | undefined;\n}\n\n// Serves a dot's builds to the example host apps on devices, which follow them and reload.\nexport interface DeviceServer {\n // What a device opens, `dotdev://connect?server=…&secret=…`, as short as it can be for a small QR\n // code. A signed link's signature is at `GET /dev/link`.\n readonly link: string;\n // Serves the build output in `dir` as the newest build. A `hot` build changed only pages, so\n // devices can run it in place, keeping each open page's state.\n publish(dir: string, options?: { hot?: boolean }): Promise<void>;\n // Tells devices the newest build failed; they keep the last good one.\n fail(message: string): void;\n close(): Promise<void>;\n}\n\ninterface Build {\n readonly files: ReadonlyMap<string, { readonly bytes: Buffer; readonly hash: string }>;\n}\n\nconst DEVICE_SCHEME = 'dotdev';\n\n// Devices ask again after this, which keeps a waiting request from outliving a quiet network.\nconst WAIT_MS = 25_000;\nconst MAX_LOG_BYTES = 1024 * 1024;\n// How long a signed link opens, so a photo of an old QR code is no use for long.\nconst LINK_LIFETIME_SECONDS = 12 * 60 * 60;\n\nexport const BUILD_MAC_HEADER = 'dot-build-mac';\n\n// What a signed link's `sig` signs, a line each.\nexport function signedLinkMessage(\n server: string,\n secret: string,\n expires: number,\n keyId: string,\n): string {\n return ['dotdev-connect', server, secret, String(expires), keyId].join('\\n');\n}\n\n// What a device sends as its bearer token, made from the link's secret so the link needn't carry it.\n// Seeing it on the network gives away nothing of the secret.\nexport function deviceToken(secret: string): string {\n return createHmac('sha256', Buffer.from(secret, 'base64url'))\n .update('dotdev-token', 'utf8')\n .digest('base64url');\n}\n\n// A signed link's signature, which `GET /dev/link` serves: what isn't secret stays out of the QR code.\nexport interface LinkSignature {\n readonly expires: number;\n readonly keyId: string;\n // Base64.\n readonly sig: string;\n}\n\n// The HMAC-SHA256 of a build listing, keyed with the link's secret: a device takes only listings\n// from whoever has the link, and each file has to match its hash in the listing.\nexport function buildMac(secret: string, body: string): string {\n return createHmac('sha256', Buffer.from(secret, 'base64url'))\n .update(`dotdev-build\\n${body}`, 'utf8')\n .digest('base64url');\n}\n\nexport async function startDeviceServer({\n address = localNetworkAddress(),\n port = 0,\n onLog = () => {},\n onConnect = () => {},\n developerKey,\n}: DeviceServerOptions = {}): Promise<DeviceServer> {\n if (address === undefined) {\n throw new Error('This computer has no local network address for devices to reach it on');\n }\n // It never goes over the network: only the link carries it.\n const secret = randomBytes(16).toString('base64url');\n const token = deviceToken(secret);\n let signature: LinkSignature | null = null;\n let build: Build = { files: new Map() };\n let generation = 0;\n // The oldest generation from which only hot builds lead to the newest: a device on it, or on a\n // later one, can take the newest in place.\n let hotSince = 0;\n let error: string | null = null;\n const waiting = new Set<() => void>();\n const connected = new Set<string>();\n\n const announce = () => {\n generation += 1;\n for (const wake of waiting) {\n wake();\n }\n };\n\n const server = createServer((request, response) => {\n const from = request.socket.remoteAddress ?? 'a device';\n // Before the token: a device needs the signature to check the link before it uses it, and the\n // signature is no secret.\n if (request.method === 'GET' && request.url === '/dev/link') {\n if (signature) {\n sendJson(response, signature);\n } else {\n response.writeHead(404).end();\n }\n return;\n }\n if (!isAuthorized(request, token)) {\n response.writeHead(401).end();\n return;\n }\n const url = new URL(request.url ?? '/', 'http://device');\n const path = url.pathname;\n if (request.method === 'GET' && path === '/dev/build') {\n if (!connected.has(from)) {\n connected.add(from);\n onConnect(from);\n }\n const files = Object.fromEntries([...build.files].map(([name, file]) => [name, file.hash]));\n const body = JSON.stringify({ generation, hotSince, files, error });\n response\n .writeHead(200, {\n 'content-type': 'application/json',\n [BUILD_MAC_HEADER]: buildMac(secret, body),\n })\n .end(body);\n } else if (request.method === 'GET' && path.startsWith('/dev/files/')) {\n if (url.searchParams.get('generation') !== String(generation)) {\n response.writeHead(409).end();\n return;\n }\n const name = path.slice('/dev/files/'.length).split('/').map(decodeURIComponent).join('/');\n const file = build.files.get(name);\n if (!file) {\n response.writeHead(404).end();\n return;\n }\n response.writeHead(200, { 'content-type': 'application/octet-stream' }).end(file.bytes);\n } else if (request.method === 'GET' && path === '/dev/changes') {\n waitForChange(response, Number(url.searchParams.get('after')));\n } else if (request.method === 'POST' && path === '/dev/log') {\n readText(request)\n .then((text) => {\n for (const line of text.split('\\n')) {\n onLog(line, from);\n }\n response.writeHead(204).end();\n })\n .catch(() => response.writeHead(413).end());\n } else {\n response.writeHead(404).end();\n }\n });\n\n const waitForChange = (response: ServerResponse, after: number) => {\n if (generation > after) {\n sendJson(response, { generation });\n return;\n }\n const wake = () => {\n clearTimeout(timer);\n waiting.delete(wake);\n sendJson(response, { generation });\n };\n const timer = setTimeout(() => {\n waiting.delete(wake);\n response.writeHead(204).end();\n }, WAIT_MS);\n waiting.add(wake);\n response.on('close', () => {\n clearTimeout(timer);\n waiting.delete(wake);\n });\n };\n\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject);\n server.listen(port, address, () => resolve());\n });\n const { port: listening } = server.address() as AddressInfo;\n const origin = `http://${address}:${listening}`;\n if (developerKey) {\n const expires = Math.floor(Date.now() / 1000) + LINK_LIFETIME_SECONDS;\n const keyId = developerKey.keyId;\n const message = signedLinkMessage(origin, secret, expires, keyId);\n signature = { expires, keyId, sig: developerKey.sign(Buffer.from(message, 'utf8')) };\n }\n\n return {\n link: `${DEVICE_SCHEME}://connect?server=${encodeURIComponent(origin)}&secret=${secret}`,\n async publish(dir, { hot = false } = {}) {\n build = await readBuild(dir);\n error = null;\n announce();\n if (!hot) {\n hotSince = generation;\n }\n },\n fail(message) {\n error = message;\n announce();\n },\n close() {\n for (const wake of waiting) {\n wake();\n }\n return new Promise((resolve) => {\n server.close(() => resolve());\n server.closeAllConnections();\n });\n },\n };\n}\n\n// The first IPv4 address on a network other than this computer's own.\nfunction localNetworkAddress(): string | undefined {\n for (const addresses of Object.values(networkInterfaces())) {\n for (const candidate of addresses ?? []) {\n if (candidate.family === 'IPv4' && !candidate.internal) {\n return candidate.address;\n }\n }\n }\n return undefined;\n}\n\n// A copy of the build output, so a device downloading it never mixes two builds.\nasync function readBuild(dir: string): Promise<Build> {\n const files = new Map<string, { bytes: Buffer; hash: string }>();\n const entries = await readdir(dir, { recursive: true, withFileTypes: true });\n for (const entry of entries) {\n // Source maps stay on the computer, and archives are for `dotctl build`.\n if (!entry.isFile() || entry.name.endsWith('.map') || entry.name.endsWith('.zip')) {\n continue;\n }\n const path = join(entry.parentPath, entry.name);\n const bytes = await readFile(path);\n const hash = createHash('sha256').update(bytes).digest('base64url');\n files.set(relative(dir, path).split(sep).join('/'), { bytes, hash });\n }\n return { files };\n}\n\nfunction isAuthorized(request: IncomingMessage, token: string): boolean {\n const given = Buffer.from(request.headers.authorization ?? '');\n const expected = Buffer.from(`Bearer ${token}`);\n return given.length === expected.length && timingSafeEqual(given, expected);\n}\n\nfunction sendJson(response: ServerResponse, body: unknown): void {\n response.writeHead(200, { 'content-type': 'application/json' }).end(JSON.stringify(body));\n}\n\nasync function readText(request: IncomingMessage): Promise<string> {\n const chunks: Buffer[] = [];\n let size = 0;\n for await (const chunk of request) {\n size += (chunk as Buffer).length;\n if (size > MAX_LOG_BYTES) {\n throw new Error('Too much log at once');\n }\n chunks.push(chunk as Buffer);\n }\n return Buffer.concat(chunks).toString('utf8');\n}\n","import { join } from 'node:path';\n\nimport type { Project } from '@monimesl/dot-compiler';\nimport { installedRuntime } from '@monimesl/dot-compiler';\nimport type { Simulator } from '@monimesl/dot-simulator';\nimport { startSimulator } from '@monimesl/dot-simulator';\n\n// `dev` and `preview` share .dot/files, so a preview finds what the dot saved while developing.\nexport function startDotSimulator(\n project: Project,\n outDir: string,\n port: number | undefined,\n): Promise<Simulator> {\n return startSimulator({\n dotDir: outDir,\n reactHostDir: installedRuntime(project.root).react.dir,\n name: project.dotJson.name,\n port,\n storageDir: join(project.root, '.dot', 'files'),\n });\n}\n","import { watch } from 'node:fs';\nimport { join, relative, resolve, sep } from 'node:path';\n\nimport type { BuildOptions } from '@monimesl/dot-compiler';\nimport { build, loadProject, PAGES_DIR } from '@monimesl/dot-compiler';\nimport { renderUnicodeCompact } from 'uqr';\n\nimport { loadDeveloperKey, missingDeveloperKeyHint } from './device/developer-key';\nimport type { DeviceServer } from './device/server';\nimport { startDeviceServer } from './device/server';\nimport { describeError, printBuildTime, printWarnings } from './report';\nimport { startDotSimulator } from './simulator';\n\ninterface DevOptions extends BuildOptions {\n port?: number | undefined;\n // Also serves the dot to the example host apps on devices, through a link and a QR code.\n device?: boolean | undefined;\n}\n\n// Editors save a file in several writes; waiting this long turns them into one rebuild.\nconst CHANGE_DEBOUNCE_MS = 50;\n\nexport async function dev({ port, device, ...options }: DevOptions): Promise<() => Promise<void>> {\n const project = await loadProject(options.root);\n const outDir = resolve(project.root, options.outDir ?? 'dist');\n const buildOptions: BuildOptions = { mode: 'development', ...options, outDir };\n\n // Returns the error to show, with paths relative to the project.\n const buildApp = async (): Promise<string | null> => {\n const started = performance.now();\n try {\n printWarnings(await build(buildOptions));\n printBuildTime(started);\n return null;\n } catch (error) {\n const message = describeError(error).replaceAll(project.root + sep, '');\n console.error(`error: ${message}`);\n return message;\n }\n };\n\n const firstError = await buildApp();\n const simulator = await startDotSimulator(project, outDir, port);\n if (firstError) {\n simulator.error(firstError);\n }\n console.log(`\\n Simulator running at: ${simulator.url}\\n`);\n\n let devices: DeviceServer | undefined;\n if (device) {\n const developerKey = await loadDeveloperKey();\n devices = await startDeviceServer({\n developerKey,\n onConnect: (from) => console.log(`A device connected from ${from}`),\n onLog: (line) => console.log(`device ${line}`),\n });\n if (firstError) {\n devices.fail(firstError);\n } else {\n await devices.publish(outDir);\n }\n printDeviceLink(devices.link);\n if (developerKey) {\n console.log(` Signed with developer key ${developerKey.keyId}\\n`);\n } else {\n console.log(`${missingDeveloperKeyHint()}\\n`);\n }\n }\n\n // What changed since the last build that worked, to tell whether the new one can run in place.\n const changed = new Set<string>();\n const pagesDir = join(project.root, PAGES_DIR) + sep;\n const rebuilds = new RebuildQueue(async () => {\n // Changes that come in while it builds count toward the next build.\n const building = [...changed];\n const error = await buildApp();\n if (error) {\n simulator.error(error);\n devices?.fail(error);\n return;\n }\n for (const path of building) {\n changed.delete(path);\n }\n // Pages run again in place; anything else, such as dot.ts, .env or statics/, starts the dot\n // again on the pages it had open.\n const hot = building.every((path) => path.startsWith(pagesDir));\n if (hot) {\n simulator.hotReload();\n } else {\n simulator.reload();\n }\n await devices?.publish(outDir, { hot });\n });\n\n let debounceTimer: NodeJS.Timeout | undefined;\n const watcher = watch(project.root, { recursive: true }, (_event, file) => {\n if (!file || isIgnored(file)) {\n return;\n }\n const path = resolve(project.root, file);\n if (path === outDir || path.startsWith(outDir + sep)) {\n return;\n }\n changed.add(path);\n clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n console.log(`Changed ${relative(project.root, path)}`);\n rebuilds.request();\n }, CHANGE_DEBOUNCE_MS);\n });\n\n return async () => {\n clearTimeout(debounceTimer);\n watcher.close();\n await Promise.all([simulator.close(), devices?.close()]);\n };\n}\n\nfunction printDeviceLink(link: string): void {\n console.log(\n ' Scan this in Dash: turn on Enable Dot Debugging in Developer Settings, then tap Live Test on\\n' +\n ' the Dots tab.\\n',\n );\n console.log(renderUnicodeCompact(link, { border: 1 }).replace(/^/gm, ' '));\n console.log(`\\n ${link}\\n`);\n}\n\n// Runs one rebuild at a time; changes that arrive mid-build trigger exactly one more once it ends.\nclass RebuildQueue {\n readonly #rebuild: () => Promise<void>;\n #building = false;\n #changedWhileBuilding = false;\n\n constructor(rebuild: () => Promise<void>) {\n this.#rebuild = rebuild;\n }\n\n request(): void {\n if (this.#building) {\n this.#changedWhileBuilding = true;\n return;\n }\n void this.#build();\n }\n\n async #build(): Promise<void> {\n this.#building = true;\n this.#changedWhileBuilding = false;\n try {\n await this.#rebuild();\n } finally {\n this.#building = false;\n }\n if (this.#changedWhileBuilding) {\n this.request();\n }\n }\n}\n\n// Dependencies and hidden folders such as .git or .dot, but not .env files, which the build reads.\nfunction isIgnored(file: string): boolean {\n return file.split(/[\\\\/]/).some((segment) => {\n if (segment === '.env' || segment.startsWith('.env.')) {\n return false;\n }\n return segment === 'node_modules' || segment.startsWith('.');\n });\n}\n","import { resolve } from 'node:path';\n\nimport type { BuildOptions } from '@monimesl/dot-compiler';\nimport { build, loadProject } from '@monimesl/dot-compiler';\nimport type { Simulator } from '@monimesl/dot-simulator';\n\nimport { printBuildTime, printWarnings } from './report';\nimport { startDotSimulator } from './simulator';\n\ninterface PreviewOptions extends BuildOptions {\n port?: number | undefined;\n}\n\n// Builds the dot as `dotctl build` does, and runs that build in the simulator: minified, with only\n// the components its pages use, as the host app gets it. It doesn't rebuild on change, since its\n// renderer has no components for a page to add.\nexport async function preview({ port, ...options }: PreviewOptions): Promise<Simulator> {\n const project = await loadProject(options.root);\n const outDir = resolve(project.root, options.outDir ?? 'dist');\n const started = performance.now();\n printWarnings(await build({ ...options, outDir, mode: 'production' }));\n printBuildTime(started);\n const simulator = await startDotSimulator(project, outDir, port);\n console.log(`\\n Simulator running at: ${simulator.url}\\n`);\n return simulator;\n}\n","import { emitKeypressEvents } from 'node:readline';\nimport { createInterface } from 'node:readline/promises';\nimport { styleText } from 'node:util';\n\nexport interface Choice<Value extends string> {\n value: Value;\n // Shown after the value, such as what a template has.\n hint?: string;\n}\n\ninterface Terminal {\n input: NodeJS.ReadStream;\n output: NodeJS.WriteStream;\n}\n\nconst TERMINAL: Terminal = { input: process.stdin, output: process.stdout };\n\nconst ERASE_BELOW = '\\x1b[0J';\nconst HIDE_CURSOR = '\\x1b[?25l';\nconst SHOW_CURSOR = '\\x1b[?25h';\nconst cursorUp = (lines: number) => `\\x1b[${lines}A`;\n\n// The person picks one of `choices` with the arrow keys and Enter. The list starts on `fallback`,\n// marked as the default, and it's `fallback` without a terminal to ask in.\nexport async function select<Value extends string>(\n question: string,\n choices: readonly Choice<Value>[],\n fallback: Value,\n { input, output }: Terminal = TERMINAL,\n): Promise<Value> {\n if (!input.isTTY) {\n return fallback;\n }\n let current = Math.max(\n 0,\n choices.findIndex((choice) => choice.value === fallback),\n );\n // Values are padded so their hints line up.\n const width = choices.some((choice) => choice.hint)\n ? Math.max(...choices.map((choice) => choice.value.length))\n : 0;\n const row = (choice: Choice<Value>, index: number) => {\n const name = choice.value.padEnd(width);\n const hint = choice.hint ? styleText('dim', ` ${choice.hint}`) : '';\n const defaultMark = choice.value === fallback ? styleText('green', ' (default)') : '';\n if (index === current) {\n return `${styleText('cyan', '❯')} ${styleText(['cyan', 'bold'], name)}${hint}${defaultMark}`;\n }\n return ` ${name}${hint}${defaultMark}`;\n };\n\n // Each drawing replaces the last: the question and its list, then the question and its answer.\n let drawnLines = 0;\n const draw = (lines: string[]) => {\n output.write(`${drawnLines ? cursorUp(drawnLines) : ''}${ERASE_BELOW}${lines.join('\\n')}\\n`);\n drawnLines = lines.length;\n };\n const drawList = () =>\n draw([\n `${styleText('bold', question)} ${styleText('dim', '↑/↓ to move, Enter to choose')}`,\n ...choices.map(row),\n ]);\n\n emitKeypressEvents(input);\n input.setRawMode(true);\n input.resume();\n output.write(HIDE_CURSOR);\n drawList();\n return new Promise((resolve) => {\n const onKeypress = (_text: string, key: { name?: string; ctrl?: boolean }) => {\n if (key.ctrl && key.name === 'c') {\n output.write(`${SHOW_CURSOR}\\n`);\n process.exit(130);\n }\n if (key.name === 'up' || key.name === 'k') {\n current = (current - 1 + choices.length) % choices.length;\n drawList();\n } else if (key.name === 'down' || key.name === 'j') {\n current = (current + 1) % choices.length;\n drawList();\n } else if (key.name === 'return' || key.name === 'enter') {\n const { value } = choices[current]!;\n input.off('keypress', onKeypress);\n input.setRawMode(false);\n input.pause();\n draw([`${styleText('bold', question)} ${styleText('cyan', value)}`]);\n output.write(SHOW_CURSOR);\n resolve(value);\n }\n };\n input.on('keypress', onKeypress);\n });\n}\n\n// A yes or no question, picked from a list like any other choice. Without a terminal, it's no.\nexport async function confirm(\n question: string,\n fallback: boolean,\n terminal: Terminal = TERMINAL,\n): Promise<boolean> {\n if (!terminal.input.isTTY) {\n return false;\n }\n const choices = [{ value: 'Yes' }, { value: 'No' }] as const;\n return (await select(question, choices, fallback ? 'Yes' : 'No', terminal)) === 'Yes';\n}\n\n// A typed answer; an empty one, or none without a terminal, is `fallback`, shown as the default.\nexport async function ask(\n question: string,\n fallback?: string,\n { input, output }: Terminal = TERMINAL,\n): Promise<string | undefined> {\n if (!input.isTTY) {\n return fallback;\n }\n const hint = fallback ? styleText('dim', ` (default: ${fallback})`) : '';\n const readline = createInterface({ input, output });\n try {\n const answer = await readline.question(`${styleText('bold', question)}${hint}: `);\n return answer.trim() || fallback;\n } catch (error) {\n // Ctrl+C or Ctrl+D\n if ((error as Error).name === 'AbortError') {\n return undefined;\n }\n throw error;\n } finally {\n readline.close();\n }\n}\n","import { spawn } from 'node:child_process';\nimport { relative } from 'node:path';\nimport { parseArgs } from 'node:util';\n\nimport type { BuildOptions } from '@monimesl/dot-compiler';\nimport { build } from '@monimesl/dot-compiler';\nimport { DEFAULT_PORT } from '@monimesl/dot-simulator';\n\nimport { create, toDisplayName, toPackageName } from './create/create';\nimport { createPage } from './create/page';\nimport type { PackageManager } from './create/package-manager';\nimport type { Template } from './create/template';\nimport { isTemplate, TEMPLATE_HINTS, TEMPLATES } from './create/template';\nimport {\n installedManagers,\n isPackageManager,\n launchedBy,\n PACKAGE_MANAGERS,\n} from './create/package-manager';\nimport { dev } from './dev';\nimport { preview } from './preview';\nimport { ask, confirm, select } from './prompts';\nimport {\n defaultDeveloperKeyPath,\n loadDeveloperKey,\n missingDeveloperKeyHint,\n} from './device/developer-key';\nimport { describeError, printBuildTime, printWarnings } from './report';\n\nconst USAGE = `Usage:\n dotctl create [dir] [--name <name>] [--template <starter|showcase>]\n [--pm <npm|pnpm|yarn|bun>]\n Create a dot (asks for its name and template):\n starter is one page to build on, showcase a page\n for each feature\n dotctl create page <path> Add a page to the dot you are in: pages/<path>/ with\n view.dml, state.ts and controller.ts\n dotctl build [root] [options] Build the minified bundles the host app runs\n dotctl dev [root] [options] Run in the browser simulator, rebuilding on change\n dotctl preview [root] [options] Build as \\`build\\` does, and run that build in the browser\n simulator, as the host app gets it\n dotctl keys show Show the public key and key ID of this computer's\n developer key, which signs the links \\`dev --device\\`\n shows, for Dash to open\n dotctl lsp [--stdio] Serve .dml files to an editor over the Language Server\n Protocol, on stdin and stdout\n\nOptions:\n --out-dir <dir> Output directory (default: <root>/dist)\n --conditions <list> Extra export conditions, comma separated\n --port <port> Simulator port for \\`dev\\` and \\`preview\\`; the next free one is used\n if taken (default: ${DEFAULT_PORT})\n --device With \\`dev\\`, also serve the dot to the Dot example apps on phones on\n the local network, through a link and QR code; they reload on each build\n -h, --help Show this message`;\n\nasync function main(argv: string[]): Promise<number> {\n const { values, positionals } = parseArgs({\n args: argv,\n allowPositionals: true,\n options: {\n 'out-dir': { type: 'string' },\n conditions: { type: 'string' },\n port: { type: 'string' },\n device: { type: 'boolean' },\n name: { type: 'string' },\n template: { type: 'string' },\n pm: { type: 'string' },\n help: { type: 'boolean', short: 'h' },\n // Editors pass it to say the server talks over stdin and stdout, which is the only way it does.\n stdio: { type: 'boolean' },\n },\n });\n\n const [command, target, pagePath] = positionals;\n if (command === 'lsp' && !values.help) {\n // Loaded only here, so the other commands start without it.\n const { serveStdio } = await import('./lsp/server');\n serveStdio();\n // The connection exits the process when the editor says so.\n return new Promise<number>(() => {});\n }\n if (command === 'keys' && !values.help) {\n return keys(target);\n }\n if (command === 'create' && target === 'page' && !values.help) {\n return addPage(pagePath);\n }\n if (command === 'create' && !values.help) {\n return createApp(target, values.name, values.template, values.pm);\n }\n if (values.help || (command !== 'build' && command !== 'dev' && command !== 'preview')) {\n console.log(USAGE);\n return values.help ? 0 : 1;\n }\n const port = values.port === undefined ? undefined : Number(values.port);\n if (port !== undefined && !Number.isInteger(port)) {\n console.error(`Invalid --port \"${values.port}\"`);\n return 1;\n }\n\n const options: BuildOptions = { root: target ?? '.' };\n if (values['out-dir'] !== undefined) {\n options.outDir = values['out-dir'];\n }\n if (values.conditions !== undefined) {\n options.conditions = values.conditions.split(',');\n }\n\n if (command === 'dev') {\n const stop = await dev({ ...options, port, device: values.device });\n await new Promise((resolve) => process.once('SIGINT', resolve));\n await stop();\n return 0;\n }\n\n if (command === 'preview') {\n if (values.device) {\n console.error('--device works only with dotctl dev');\n return 1;\n }\n const simulator = await preview({ ...options, port });\n await new Promise((resolve) => process.once('SIGINT', resolve));\n await simulator.close();\n return 0;\n }\n\n const started = performance.now();\n const result = await build(options);\n for (const file of result.files.filter((file) => !file.endsWith('.map'))) {\n console.log(` ${relative(process.cwd(), file)}`);\n }\n printWarnings(result);\n printBuildTime(started);\n return 0;\n}\n\nasync function keys(action: string | undefined): Promise<number> {\n if (action !== 'show') {\n console.error('dotctl keys show');\n return 1;\n }\n const path = defaultDeveloperKeyPath();\n const key = await loadDeveloperKey(path);\n if (!key) {\n console.error(missingDeveloperKeyHint(path));\n return 1;\n }\n console.log(`\\n Public key: ${key.publicKey}\\n Key ID: ${key.keyId}\\n`);\n return 0;\n}\n\nasync function addPage(path: string | undefined): Promise<number> {\n if (path === undefined) {\n console.error(\n 'Which page? dotctl create page <path>, such as dotctl create page cart/checkout',\n );\n return 1;\n }\n if (path.startsWith('/')) {\n path = path.substring(1);\n }\n if (path.endsWith('/')) {\n path = path.substring(0, path.length - 1);\n }\n const { files, listing } = await createPage(path, process.cwd());\n for (const file of files) {\n console.log(` ${relative(process.cwd(), file)}`);\n }\n if (listing === 'added') {\n console.log(`\\nListed '${path}' in dot.ts.`);\n } else if (listing === 'manual') {\n console.log(`\\nList it in dot.ts to build it: defineDot({ pages: [..., '${path}'] })`);\n }\n return 0;\n}\n\nasync function createApp(\n dir: string | undefined,\n name: string | undefined,\n templateFlag: string | undefined,\n packageManagerFlag: string | undefined,\n): Promise<number> {\n if (templateFlag !== undefined && !isTemplate(templateFlag)) {\n console.error(`--template must be one of ${TEMPLATES.join(', ')}`);\n return 1;\n }\n if (packageManagerFlag !== undefined && !isPackageManager(packageManagerFlag)) {\n console.error(`--pm must be one of ${PACKAGE_MANAGERS.join(', ')}`);\n return 1;\n }\n name ??= await ask('Dot name', dir ? toDisplayName(dir) : 'Dot');\n if (!name) {\n console.error('A dot needs a name: dotctl create [dir] --name \"My Dot\"');\n return 1;\n }\n const template = templateFlag ?? (await chooseTemplate());\n const { root, workspace } = await create({\n name,\n dir: dir ?? toPackageName(name),\n template,\n });\n const path = relative(process.cwd(), root) || '.';\n console.log(`\\nCreated ${name} in ${path}`);\n if (workspace) {\n console.log('It is inside the Dot repository, so it uses the workspace packages.');\n }\n\n // A command cannot change its shell's directory, so it offers to carry on inside the new dot instead.\n let packageManager: PackageManager = workspace ? 'pnpm' : (packageManagerFlag ?? launchedBy());\n console.log();\n if (await confirm('Install and start it now?', true)) {\n if (!workspace && !packageManagerFlag) {\n packageManager = await choosePackageManager(packageManager);\n }\n const installed = await run(packageManager, ['install'], root);\n if (installed === 0) {\n return run(packageManager, ['run', 'dev'], root);\n }\n console.error(`\\n${packageManager} install failed.`);\n }\n console.log(\n `\\nNext:\\n\\n cd ${path}\\n ${packageManager} install\\n ${packageManager} run dev\\n`,\n );\n return 0;\n}\n\nfunction run(command: string, args: string[], cwd: string): Promise<number> {\n return new Promise((resolvePromise, reject) => {\n const child = spawn(command, args, {\n cwd,\n stdio: 'inherit',\n shell: process.platform === 'win32',\n });\n child.on('error', reject);\n child.on('exit', (code) => resolvePromise(code ?? 1));\n });\n}\n\nfunction chooseTemplate(): Promise<Template> {\n console.log();\n return select(\n 'Template?',\n TEMPLATES.map((template) => ({ value: template, hint: TEMPLATE_HINTS[template] })),\n 'starter',\n );\n}\n\nasync function choosePackageManager(suggested: PackageManager): Promise<PackageManager> {\n const installed = installedManagers();\n if (installed.length < 2) {\n return installed[0] ?? suggested;\n }\n const fallback = installed.includes(suggested) ? suggested : installed[0]!;\n return select(\n 'Package manager?',\n installed.map((manager) => ({ value: manager })),\n fallback,\n );\n}\n\nmain(process.argv.slice(2)).then(\n (code) => process.exit(code),\n (error: unknown) => {\n console.error(`error: ${describeError(error)}`);\n process.exit(1);\n },\n);\n"],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { t as findDotRoot } from "./cli.js";
|
|
1
2
|
import { createRequire } from "node:module";
|
|
2
3
|
import { dirname, join } from "node:path";
|
|
3
4
|
import { CONTROL_FLOW_TAGS, DATA_PREFIX, ROOT_TAG, canHaveChildren, compileView, dataKey, listStatics } from "@monimesl/dot-compiler";
|
|
@@ -10208,10 +10209,6 @@ function withLoop(scope, item, itemShape, index) {
|
|
|
10208
10209
|
//#endregion
|
|
10209
10210
|
//#region src/lsp/check-members.ts
|
|
10210
10211
|
const BINDING = /{{([\s\S]*?)}}/g;
|
|
10211
|
-
/**
|
|
10212
|
-
* Finds the bindings that read a property their value doesn't have, going by the types in state.ts:
|
|
10213
|
-
* `{{ tap.nmae }}` where each tap is a `Tap`. A value whose type isn't known is not checked.
|
|
10214
|
-
*/
|
|
10215
10212
|
function checkMembers(text, state) {
|
|
10216
10213
|
let nodes;
|
|
10217
10214
|
try {
|
|
@@ -10297,7 +10294,7 @@ function memberOffset(bindingOffset, binding, name) {
|
|
|
10297
10294
|
}
|
|
10298
10295
|
//#endregion
|
|
10299
10296
|
//#region src/lsp/markup.ts
|
|
10300
|
-
const
|
|
10297
|
+
const TAG_NAME = /[A-Za-z0-9_.:-]/;
|
|
10301
10298
|
const ATTRIBUTE_NAME_END = /[\s=/>"']/;
|
|
10302
10299
|
const WHITESPACE = /\s/;
|
|
10303
10300
|
function markupContextAt(text, offset) {
|
|
@@ -10309,7 +10306,7 @@ function markupContextAt(text, offset) {
|
|
|
10309
10306
|
index = end + 3;
|
|
10310
10307
|
} else if (text.startsWith("</", index)) {
|
|
10311
10308
|
const nameStart = index + 2;
|
|
10312
|
-
const nameEnd = readWhile(text, nameStart,
|
|
10309
|
+
const nameEnd = readWhile(text, nameStart, TAG_NAME);
|
|
10313
10310
|
const close = text.indexOf(">", nameEnd);
|
|
10314
10311
|
if (offset <= nameEnd || close < 0 || close >= offset) return {
|
|
10315
10312
|
kind: "closeTag",
|
|
@@ -10336,7 +10333,7 @@ function markupContextAt(text, offset) {
|
|
|
10336
10333
|
}
|
|
10337
10334
|
function readTag(text, start, offset, open) {
|
|
10338
10335
|
const nameStart = start + 1;
|
|
10339
|
-
const nameEnd = readWhile(text, nameStart,
|
|
10336
|
+
const nameEnd = readWhile(text, nameStart, TAG_NAME);
|
|
10340
10337
|
if (offset <= nameEnd) return {
|
|
10341
10338
|
context: {
|
|
10342
10339
|
kind: "tagName",
|
|
@@ -10477,7 +10474,6 @@ function wordAt(text, offset, pattern) {
|
|
|
10477
10474
|
}
|
|
10478
10475
|
//#endregion
|
|
10479
10476
|
//#region src/lsp/features.ts
|
|
10480
|
-
const TAG_NAME = /[A-Za-z0-9_.:-]/;
|
|
10481
10477
|
const ATTRIBUTE_NAME = /[^\s=/>"']/;
|
|
10482
10478
|
const IMAGE_ATTRIBUTES = ["src", "image"];
|
|
10483
10479
|
const IDENTIFIER = /[\w$]/;
|
|
@@ -10509,14 +10505,15 @@ function complete(document, offset, page, { components }) {
|
|
|
10509
10505
|
start: document.positionAt(start),
|
|
10510
10506
|
end: document.positionAt(offset)
|
|
10511
10507
|
});
|
|
10508
|
+
const replacing = (start, items) => items.map((item) => ({
|
|
10509
|
+
...item,
|
|
10510
|
+
textEdit: {
|
|
10511
|
+
range: range(start),
|
|
10512
|
+
newText: item.insertText ?? item.label
|
|
10513
|
+
}
|
|
10514
|
+
}));
|
|
10512
10515
|
switch (context.kind) {
|
|
10513
|
-
case "tagName": return tagCompletions(context.parents, components)
|
|
10514
|
-
...item,
|
|
10515
|
-
textEdit: {
|
|
10516
|
-
range: range(context.start),
|
|
10517
|
-
newText: item.insertText ?? item.label
|
|
10518
|
-
}
|
|
10519
|
-
}));
|
|
10516
|
+
case "tagName": return replacing(context.start, tagCompletions(context.parents, components));
|
|
10520
10517
|
case "closeTag":
|
|
10521
10518
|
if (!context.open) return [];
|
|
10522
10519
|
return [{
|
|
@@ -10527,34 +10524,16 @@ function complete(document, offset, page, { components }) {
|
|
|
10527
10524
|
newText: `${context.open.tag}>`
|
|
10528
10525
|
}
|
|
10529
10526
|
}];
|
|
10530
|
-
case "attributeName": return attributeCompletions(context.element, components)
|
|
10531
|
-
...item,
|
|
10532
|
-
textEdit: {
|
|
10533
|
-
range: range(context.start),
|
|
10534
|
-
newText: item.insertText ?? item.label
|
|
10535
|
-
}
|
|
10536
|
-
}));
|
|
10527
|
+
case "attributeName": return replacing(context.start, attributeCompletions(context.element, components));
|
|
10537
10528
|
case "attributeValue": {
|
|
10538
10529
|
const scope = shapeScope(context.loopVariables, page);
|
|
10539
|
-
return valueCompletions(context.element, context.attribute, page, scope, components)
|
|
10540
|
-
...item,
|
|
10541
|
-
textEdit: {
|
|
10542
|
-
range: range(context.start),
|
|
10543
|
-
newText: item.label
|
|
10544
|
-
}
|
|
10545
|
-
}));
|
|
10530
|
+
return replacing(context.start, valueCompletions(context.element, context.attribute, page, scope, components));
|
|
10546
10531
|
}
|
|
10547
10532
|
case "binding": {
|
|
10548
10533
|
const typed = text.slice(context.start, offset);
|
|
10549
10534
|
const wordStart = offset - (/[A-Za-z_$][\w$]*$/.exec(typed)?.[0] ?? "").length;
|
|
10550
10535
|
const scope = shapeScope(context.loopVariables, page);
|
|
10551
|
-
return (isMember(text, context.start, wordStart) ? memberCompletions(ownerOf(text, context.start, wordStart, scope)) : bindingCompletions(context.loopVariables, page, scope))
|
|
10552
|
-
...item,
|
|
10553
|
-
textEdit: {
|
|
10554
|
-
range: range(wordStart),
|
|
10555
|
-
newText: item.label
|
|
10556
|
-
}
|
|
10557
|
-
}));
|
|
10536
|
+
return replacing(wordStart, isMember(text, context.start, wordStart) ? memberCompletions(ownerOf(text, context.start, wordStart, scope)) : bindingCompletions(context.loopVariables, page, scope));
|
|
10558
10537
|
}
|
|
10559
10538
|
default: return [];
|
|
10560
10539
|
}
|
|
@@ -10818,21 +10797,15 @@ function symbolAt(text, offset, page) {
|
|
|
10818
10797
|
} : null;
|
|
10819
10798
|
}
|
|
10820
10799
|
const variable = [...context.loopVariables].reverse().find((candidate) => candidate.name === name);
|
|
10821
|
-
if (variable) {
|
|
10822
|
-
|
|
10823
|
-
|
|
10824
|
-
|
|
10825
|
-
|
|
10826
|
-
|
|
10827
|
-
|
|
10828
|
-
|
|
10829
|
-
|
|
10830
|
-
variable
|
|
10831
|
-
},
|
|
10832
|
-
start,
|
|
10833
|
-
end
|
|
10834
|
-
};
|
|
10835
|
-
}
|
|
10800
|
+
if (variable) return {
|
|
10801
|
+
symbol: {
|
|
10802
|
+
kind: "loopVariable",
|
|
10803
|
+
variable,
|
|
10804
|
+
shape: scope?.loops.get(name)
|
|
10805
|
+
},
|
|
10806
|
+
start,
|
|
10807
|
+
end
|
|
10808
|
+
};
|
|
10836
10809
|
return {
|
|
10837
10810
|
symbol: {
|
|
10838
10811
|
kind: "state",
|
|
@@ -10955,7 +10928,7 @@ function diagnosticAt(document, start, message, severity) {
|
|
|
10955
10928
|
//#region src/lsp/page.ts
|
|
10956
10929
|
async function readPage(viewFile, readText, stateTypes) {
|
|
10957
10930
|
const dir = dirname(viewFile);
|
|
10958
|
-
const root =
|
|
10931
|
+
const root = findDotRoot(dir);
|
|
10959
10932
|
const [state, controller, statics] = await Promise.all([
|
|
10960
10933
|
readState(join(dir, "state.ts"), root, readText, stateTypes),
|
|
10961
10934
|
readController(join(dir, "controller.ts"), readText),
|
|
@@ -10971,20 +10944,18 @@ async function readPage(viewFile, readText, stateTypes) {
|
|
|
10971
10944
|
async function readState(file, root, readText, stateTypes) {
|
|
10972
10945
|
const text = await readText(file);
|
|
10973
10946
|
if (text === void 0) return;
|
|
10974
|
-
let keys;
|
|
10975
|
-
let offsets;
|
|
10976
10947
|
try {
|
|
10977
|
-
|
|
10948
|
+
const { keys, keyOffsets } = parseStateModule(text, file);
|
|
10949
|
+
return {
|
|
10950
|
+
file,
|
|
10951
|
+
text,
|
|
10952
|
+
keys,
|
|
10953
|
+
offsets: keyOffsets,
|
|
10954
|
+
shapes: readShapes(stateTypes, root, file, text)
|
|
10955
|
+
};
|
|
10978
10956
|
} catch {
|
|
10979
10957
|
return;
|
|
10980
10958
|
}
|
|
10981
|
-
return {
|
|
10982
|
-
file,
|
|
10983
|
-
text,
|
|
10984
|
-
keys,
|
|
10985
|
-
offsets,
|
|
10986
|
-
shapes: readShapes(stateTypes, root, file, text)
|
|
10987
|
-
};
|
|
10988
10959
|
}
|
|
10989
10960
|
function readShapes(stateTypes, root, file, text) {
|
|
10990
10961
|
try {
|
|
@@ -11009,14 +10980,6 @@ async function readController(file, readText) {
|
|
|
11009
10980
|
return;
|
|
11010
10981
|
}
|
|
11011
10982
|
}
|
|
11012
|
-
function findRoot(dir) {
|
|
11013
|
-
while (true) {
|
|
11014
|
-
if (existsSync(join(dir, "dot.json"))) return dir;
|
|
11015
|
-
const parent = dirname(dir);
|
|
11016
|
-
if (parent === dir) return;
|
|
11017
|
-
dir = parent;
|
|
11018
|
-
}
|
|
11019
|
-
}
|
|
11020
10983
|
//#endregion
|
|
11021
10984
|
//#region src/lsp/state-types.ts
|
|
11022
10985
|
const MAX_DEPTH = 8;
|
|
@@ -11028,10 +10991,6 @@ const FALLBACK_OPTIONS = {
|
|
|
11028
10991
|
noEmit: true,
|
|
11029
10992
|
skipLibCheck: true
|
|
11030
10993
|
};
|
|
11031
|
-
/**
|
|
11032
|
-
* Reads the shapes of state.ts files. Each dot keeps one TypeScript language service, so reading
|
|
11033
|
-
* again after an edit rechecks only what changed.
|
|
11034
|
-
*/
|
|
11035
10994
|
var StateTypes = class {
|
|
11036
10995
|
#projects = /* @__PURE__ */ new Map();
|
|
11037
10996
|
read(root, file, text) {
|
|
@@ -11159,11 +11118,6 @@ const WATCHED = [
|
|
|
11159
11118
|
"**/dot.json",
|
|
11160
11119
|
"**/statics/**"
|
|
11161
11120
|
];
|
|
11162
|
-
/**
|
|
11163
|
-
* Serves .dml files over the Language Server Protocol: completion, hover, go to definition and
|
|
11164
|
-
* the compiler's own errors and warnings. It runs from the dot's own installed `dotctl`, so what
|
|
11165
|
-
* it knows always matches the Dot version the dot builds with.
|
|
11166
|
-
*/
|
|
11167
11121
|
function startLanguageServer(connection) {
|
|
11168
11122
|
const documents = new import_main.TextDocuments(TextDocument);
|
|
11169
11123
|
const features = { components: builtinRegistry };
|
|
@@ -11253,11 +11207,10 @@ function startLanguageServer(connection) {
|
|
|
11253
11207
|
connection.listen();
|
|
11254
11208
|
return connection;
|
|
11255
11209
|
}
|
|
11256
|
-
/** Serves an editor that started `dotctl lsp`, over stdin and stdout. */
|
|
11257
11210
|
function serveStdio() {
|
|
11258
11211
|
return startLanguageServer((0, import_node.createConnection)(import_node.ProposedFeatures.all, process.stdin, process.stdout));
|
|
11259
11212
|
}
|
|
11260
11213
|
//#endregion
|
|
11261
11214
|
export { serveStdio };
|
|
11262
11215
|
|
|
11263
|
-
//# sourceMappingURL=server-
|
|
11216
|
+
//# sourceMappingURL=server-BfPAnsSf.js.map
|