This commit is contained in:
Drew Powers 2021-05-03 12:26:10 -06:00 committed by GitHub
parent c93201a909
commit 94038d3297
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
96 changed files with 2777 additions and 3003 deletions

View file

@ -6,8 +6,5 @@
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": [
"@example/*",
"www"
]
"ignore": ["@example/*", "www"]
}

1
.prettierignore Normal file
View file

@ -0,0 +1 @@
**/dist/*

10
.vscode/launch.json vendored
View file

@ -7,12 +7,8 @@
"request": "launch",
"name": "Launch Client",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceRoot}/vscode"
],
"outFiles": [
"${workspaceRoot}/vscode/dist/**/*.js"
],
"args": ["--extensionDevelopmentPath=${workspaceRoot}/vscode"],
"outFiles": ["${workspaceRoot}/vscode/dist/**/*.js"],
"preLaunchTask": {
"type": "npm",
"script": "build:extension"
@ -25,7 +21,7 @@
"port": 6040,
"restart": true,
"outFiles": ["${workspaceRoot}/vscode/dist/**/*.js"]
},
}
],
"compounds": [
{

5
.vscode/tasks.json vendored
View file

@ -1,4 +1,3 @@
{
"version": "2.0.0",
"tasks": [
@ -10,9 +9,7 @@
"panel": "dedicated",
"reveal": "never"
},
"problemMatcher": [
"$tsc"
]
"problemMatcher": ["$tsc"]
}
]
}

View file

@ -43,7 +43,6 @@ npm run build
To deploy your Astro site to production, upload the contents of `/dist` to your favorite static site host.
## 🥾 Guides
### 🚀 Basic Usage
@ -67,7 +66,7 @@ Even though nearly-everything [is configurable][docs-config], we recommend start
Routing happens in `src/pages/*`. Every `.astro` or `.md.astro` file in this folder corresponds with a public URL. For example:
| Local file | Public URL |
| :--------------------------------------- | :------------------------------ |
| :------------------------------------- | :------------------------------ |
| `src/pages/index.astro` | `/index.html` |
| `src/pages/post/my-blog-post.md.astro` | `/post/my-blog-post/index.html` |
@ -164,10 +163,10 @@ Astro will automatically create a `/sitemap.xml` for you for SEO! Be sure to set
👉 [**Collections API**][docs-collections]
## ⚙️ Config
👉 [**`astro.config.mjs` Reference**][docs-config]
## 📚 API
👉 [**Full API Reference**][docs-api]

View file

@ -36,7 +36,7 @@ Runs the Astro development server. This starts an HTTP server that responds to r
See the [dev server](./dev.md) docs for more information on how the dev server works.
__Flags__
**Flags**
##### `--port`

View file

@ -14,7 +14,7 @@ The dev server will serve the following special routes:
### /400
This is a custom __400__ status code page. You can add this route by adding a page component to your `src/pages` folder:
This is a custom **400** status code page. You can add this route by adding a page component to your `src/pages` folder:
```
├── src/
@ -27,13 +27,10 @@ For any URL you visit that doesn't have a corresponding page, the `400.astro` fi
### /500
This is a custom __500__ status code page. You can add this route by adding a page component to your `src/pages` folder:
This is a custom **500** status code page. You can add this route by adding a page component to your `src/pages` folder:
```astro
├── src/
│ ├── components/
│ └── pages/
│ └── 500.astro
├── src/ │ ├── components/ │ └── pages/ │ └── 500.astro
```
This page is used any time an error occurs in the dev server.

View file

@ -117,7 +117,7 @@ export let name;
`.astro` files can end up looking very similar to `.jsx` files, but there are a few key differences. Here's a comparison between the two formats.
| Feature | Astro | JSX |
|------------------------- |------------------------------------------ |---------------------------------------------------- |
| ---------------------------- | ---------------------------------------- | -------------------------------------------------- |
| File extension | `.astro` | `.jsx` or `.tsx` |
| User-Defined Components | `<Capitalized>` | `<Capitalized>` |
| Expression Syntax | `{}` | `{}` |
@ -137,5 +137,4 @@ export let name;
### TODO: Composition (Slots)
[code-ext]: https://marketplace.visualstudio.com/items?itemName=astro-build.astro

View file

@ -3,18 +3,18 @@ import { useState } from 'preact/hooks';
/** a counter written in Preact */
export default function PreactCounter({ children }) {
const [count, setCount] = useState(0)
const add = () => setCount(i => i + 1);
const subtract = () => setCount(i => i - 1);
const [count, setCount] = useState(0);
const add = () => setCount((i) => i + 1);
const subtract = () => setCount((i) => i - 1);
return <>
return (
<>
<div className="counter">
<button onClick={subtract}>-</button>
<pre>{count}</pre>
<button onClick={add}>+</button>
</div>
<div className="children">
{children}
</div>
<div className="children">{children}</div>
</>
);
}

View file

@ -2,18 +2,18 @@ import React, { useState } from 'react';
/** a counter written in React */
export default function ReactCounter({ children }) {
const [count, setCount] = useState(0)
const add = () => setCount(i => i + 1);
const subtract = () => setCount(i => i - 1);
const [count, setCount] = useState(0);
const add = () => setCount((i) => i + 1);
const subtract = () => setCount((i) => i - 1);
return <>
return (
<>
<div className="counter">
<button onClick={subtract}>-</button>
<pre>{count}</pre>
<button onClick={add}>+</button>
</div>
<div className="children">
{children}
</div>
<div className="children">{children}</div>
</>
);
}

View file

@ -1,8 +1,8 @@
# Astro Demo
## Getting setup
## Getting set up
1. Checkout Astro at: https://github.com/snowpackjs/astro
1. Check out Astro at: https://github.com/snowpackjs/astro
1. Install and build Astro:

View file

@ -1,4 +1,4 @@
import {h} from 'preact';
import { h } from 'preact';
export default function CompanyLogo({ user }) {
return (

View file

@ -117,5 +117,9 @@ function PluginSearchPageLive() {
}
export default function PluginSearchPage(props) {
return import.meta.env.astro ? <div>Loading...</div> : <PluginSearchPageLive {...props} />
return import.meta.env.astro ? (
<div>Loading...</div>
) : (
<PluginSearchPageLive {...props} />
);
}

View file

@ -1,17 +1,20 @@
import docsearch from 'docsearch.js/dist/cdn/docsearch.min.js';
customElements.define('doc-search', class extends HTMLElement {
customElements.define(
'doc-search',
class extends HTMLElement {
connectedCallback() {
if(!this._setup) {
if (!this._setup) {
const apiKey = this.getAttribute('api-key');
const selector = this.getAttribute('selector');
docsearch({
apiKey: apiKey,
indexName: 'snowpack',
inputSelector: selector,
debug: true // Set debug to true if you want to inspect the dropdown
debug: true, // Set debug to true if you want to inspect the dropdown
});
this._setup = true;
}
}
});
},
);

View file

@ -1,2 +1,4 @@
const snowpackManifest = JSON.parse(fs.readFileSync(path.join(__dirname, '../../snowpack/package.json'), 'utf8'));
const snowpackManifest = JSON.parse(
fs.readFileSync(path.join(__dirname, '../../snowpack/package.json'), 'utf8'),
);
export default snowpackManifest.version;

View file

@ -1,8 +1,5 @@
{
"ignoreChanges": [
"**/test/**",
"**/*.md"
],
"ignoreChanges": ["**/test/**", "**/*.md"],
"useWorkspaces": true,
"version": "4.0.0"
}

View file

@ -5,11 +5,11 @@
"release": "yarn build && yarn changeset publish",
"build": "yarn build:core",
"build:core": "lerna run build --scope astro --scope astro-parser --scope create-astro",
"format": "prettier -w '**/*.{js,jsx,ts,tsx,md,json}'",
"lint": "eslint 'packages/**/*.ts'",
"test": "yarn test:core && yarn test:prettier",
"test:core": "cd packages/astro && npm test",
"test:prettier": "cd tools/prettier-plugin-astro && npm test",
"format": "prettier -w '**/*.{js,jsx,ts,tsx,md,json}'"
"test:prettier": "cd tools/prettier-plugin-astro && npm test"
},
"workspaces": [
"packages/*",

View file

@ -16,7 +16,6 @@ import { generateRSS } from './build/rss.js';
import { generateSitemap } from './build/sitemap.js';
import { collectStatics } from './build/static.js';
import { canonicalURL } from './build/util.js';
import { pathToFileURL } from 'node:url';
const { mkdir, readFile, writeFile } = fsPromises;
@ -69,8 +68,8 @@ async function writeFilep(outPath: URL, bytes: string | Buffer, encoding: 'utf8'
interface WriteResultOptions {
srcPath: string;
result: LoadResult;
outPath: URL,
encoding: null|'utf8'
outPath: URL;
encoding: null | 'utf8';
}
/** Utility for writing a build result to disk */

View file

@ -31,10 +31,10 @@ interface CLIState {
/** Determine which action the user requested */
function resolveArgs(flags: Arguments): CLIState {
const options: CLIState['options'] = {
projectRoot: typeof flags.projectRoot === 'string' ? flags.projectRoot: undefined,
projectRoot: typeof flags.projectRoot === 'string' ? flags.projectRoot : undefined,
sitemap: typeof flags.sitemap === 'boolean' ? flags.sitemap : undefined,
port: typeof flags.port === 'number' ? flags.port : undefined,
config: typeof flags.config === 'string' ? flags.config : undefined
config: typeof flags.config === 'string' ? flags.config : undefined,
};
if (flags.version) {

View file

@ -15,7 +15,7 @@ import { encodeAstroMdx } from './markdown/micromark-mdx-astro.js';
import { transform } from './transform/index.js';
import { codegen } from './codegen/index.js';
export { scopeRule } from './transform/postcss-scoped-styles/index.js'
export { scopeRule } from './transform/postcss-scoped-styles/index.js';
/** Return Astro internal import URL */
function internalImport(internalPath: string) {

View file

@ -26,8 +26,8 @@ function validateConfig(config: any): void {
}
}
if(typeof config.devOptions?.port !== 'number') {
throw new Error(`[astro config] devOptions.port: Expected number, received ${type(config.devOptions?.port)}`)
if (typeof config.devOptions?.port !== 'number') {
throw new Error(`[astro config] devOptions.port: Expected number, received ${type(config.devOptions?.port)}`);
}
}

View file

@ -75,7 +75,7 @@ export default async function dev(astroConfig: AstroConfig) {
res.statusCode = 500;
let errorResult = await runtime.load(`/500?error=${encodeURIComponent(result.error.stack || result.error.toString())}`);
if(errorResult.statusCode === 200) {
if (errorResult.statusCode === 200) {
if (errorResult.contentType) {
res.setHeader('Content-Type', errorResult.contentType);
}

View file

@ -49,6 +49,6 @@ export const childrenToH = moize.deep(function childrenToH(renderer: ComponentRe
};
return tree.map((subtree) => {
if (subtree.type === 'text') return JSON.stringify(subtree.value);
return toH(innerH, subtree).__SERIALIZED
return toH(innerH, subtree).__SERIALIZED;
});
});

View file

@ -16,7 +16,7 @@ export const defaultLogDestination = new Writable({
dest = process.stdout;
}
let type = event.type;
if(type !== null) {
if (type !== null) {
if (event.level === 'info') {
type = bold(blue(type));
} else if (event.level === 'error') {
@ -135,10 +135,10 @@ export const logger = {
};
// For silencing libraries that go directly to console.warn
export function trapWarn(cb: (...args: any[]) => void = () =>{}) {
export function trapWarn(cb: (...args: any[]) => void = () => {}) {
const warn = console.warn;
console.warn = function(...args: any[]) {
console.warn = function (...args: any[]) {
cb(...args);
};
return () => console.warn = warn;
return () => (console.warn = warn);
}

View file

@ -200,14 +200,14 @@ async function load(config: RuntimeConfig, rawPathname: string | undefined): Pro
// For first release query params are not passed to components.
// An exception is made for dev server specific routes.
if(reqPath !== '/500') {
if (reqPath !== '/500') {
requestURL.search = '';
}
let html = (await mod.exports.__renderPage({
request: {
// params should go here when implemented
url: requestURL
url: requestURL,
},
children: [],
props: { collection },

View file

@ -92,14 +92,14 @@ export function searchForPage(url: URL, astroRoot: URL): SearchResult {
}
}
if(reqPath === '/500') {
if (reqPath === '/500') {
return {
statusCode: 200,
location: {
fileURL: new URL('./frontend/500.astro', import.meta.url),
snowpackURL: `/_astro_internal/500.astro.js`
snowpackURL: `/_astro_internal/500.astro.js`,
},
pathname: reqPath
pathname: reqPath,
};
}

View file

@ -4,7 +4,6 @@ import * as assert from 'uvu/assert';
import { loadConfig } from '#astro/config';
import { createRuntime } from '#astro/runtime';
const DType = suite('doctype');
let runtime, setupError;

View file

@ -12,7 +12,7 @@ ConfigPath('can be passed via --config', async (context) => {
process.stdout.setEncoding('utf8');
for await (const chunk of process.stdout) {
if(/Server started/.test(chunk)) {
if (/Server started/.test(chunk)) {
break;
}
}

View file

@ -17,7 +17,7 @@ ConfigPort('can be specified via --port flag', async (context) => {
process.stdout.setEncoding('utf8');
for await (const chunk of process.stdout) {
if(/Local:/.test(chunk)) {
if (/Local:/.test(chunk)) {
assert.ok(/:3002/.test(chunk), 'Using the right port');
break;
}

View file

@ -1,5 +1,5 @@
import { h } from 'preact';
export default function PreactComponent({ children }) {
return <div id="preact">{children}</div>
return <div id="preact">{children}</div>;
}

View file

@ -1,9 +1,9 @@
import React from 'react';
export default function() {
export default function () {
return (
<div>
<button type="button">Increment -</button>
</div>
)
);
}

View file

@ -1,5 +1,5 @@
import { h } from 'preact';
export default function({ name }) {
return <div id={name}>{name}</div>
export default function ({ name }) {
return <div id={name}>{name}</div>;
}

View file

@ -1,7 +1,5 @@
import { h } from 'preact';
export default function(props) {
return (
<div id="fallback">{import.meta.env.astro ? 'static' : 'dynamic'}</div>
);
};
export default function (props) {
return <div id="fallback">{import.meta.env.astro ? 'static' : 'dynamic'}</div>;
}

View file

@ -1,5 +1,5 @@
import { h } from 'preact';
export default function() {
return <div id="test">Testing</div>
export default function () {
return <div id="test">Testing</div>;
}

View file

@ -1,5 +1,5 @@
import { h } from 'preact';
export default function({ name }) {
return <div id="test">Hello {name}</div>
export default function ({ name }) {
return <div id="test">Hello {name}</div>;
}

View file

@ -4,4 +4,3 @@ import cheerio from 'cheerio';
export function doc(html) {
return cheerio.load(html);
}

View file

@ -1,6 +1,7 @@
# create-astro
## 0.1.0
### Minor Changes
- ed63132: Added **interactive mode** with a fesh new UI.

View file

@ -1,12 +1,15 @@
# create-astro
## Scaffolding for Astro projects
**With NPM:**
```bash
npm init astro
```
**With Yarn:**
```bash
yarn create astro
```

View file

@ -1,4 +1,4 @@
import React, {FC, useEffect} from 'react';
import React, { FC, useEffect } from 'react';
import { prepareTemplate, isEmpty, emptyDir } from '../utils';
import Header from './Header';
import Install from './Install';
@ -8,7 +8,7 @@ import Confirm from './Confirm';
import Finalize from './Finalize';
interface Context {
use: 'npm'|'yarn';
use: 'npm' | 'yarn';
run: boolean;
projectExists?: boolean;
force?: boolean;
@ -20,44 +20,49 @@ interface Context {
const getStep = ({ projectName, projectExists: exists, template, force, ready }: Context) => {
switch (true) {
case !projectName: return {
case !projectName:
return {
key: 'projectName',
Component: ProjectName
Component: ProjectName,
};
case projectName && exists === true && typeof force === 'undefined': return {
case projectName && exists === true && typeof force === 'undefined':
return {
key: 'force',
Component: Confirm
}
case (exists === false || force) && !template: return {
Component: Confirm,
};
case (exists === false || force) && !template:
return {
key: 'template',
Component: Template
Component: Template,
};
case !ready: return {
case !ready:
return {
key: 'install',
Component: Install
Component: Install,
};
default: return {
default:
return {
key: 'final',
Component: Finalize
Component: Finalize,
};
}
}
}
};
const App: FC<{ context: Context }> = ({ context }) => {
const [state, setState] = React.useState(context);
const step = React.useRef(getStep(context));
const onSubmit = (value: string|boolean) => {
const onSubmit = (value: string | boolean) => {
const { key } = step.current;
const newState = { ...state, [key]: value };
step.current = getStep(newState)
setState(newState)
}
step.current = getStep(newState);
setState(newState);
};
useEffect(() => {
let isSubscribed = true
let isSubscribed = true;
if (state.projectName && typeof state.projectExists === 'undefined') {
const newState = { ...state, projectExists: !isEmpty(state.projectName) };
step.current = getStep(newState)
step.current = getStep(newState);
if (isSubscribed) {
setState(newState);
}
@ -67,8 +72,8 @@ const App: FC<{ context: Context }> = ({ context }) => {
if (state.force) emptyDir(state.projectName);
prepareTemplate(context.use, state.template, state.projectName).then(() => {
if (isSubscribed) {
setState(v => {
const newState = {...v, ready: true };
setState((v) => {
const newState = { ...v, ready: true };
step.current = getStep(newState);
return newState;
});
@ -78,16 +83,16 @@ const App: FC<{ context: Context }> = ({ context }) => {
return () => {
isSubscribed = false;
}
};
}, [state]);
const { Component } = step.current;
return (
<>
<Header context={state}/>
<Header context={state} />
<Component context={state} onSubmit={onSubmit} />
</>
)
);
};
export default App;

View file

@ -31,7 +31,7 @@ const Confirm: FC<{ message?: any; context: any; onSubmit: (value: boolean) => v
items={[
{
value: false,
label: 'no'
label: 'no',
},
{
value: true,

View file

@ -2,4 +2,4 @@ import React from 'react';
import { Text } from 'ink';
import { isWin } from '../utils';
export default ({ children }) => isWin() ? null : <Text>{children}</Text>
export default ({ children }) => (isWin() ? null : <Text>{children}</Text>);

View file

@ -2,8 +2,11 @@ import React, { FC } from 'react';
import { Box, Text } from 'ink';
import { isDone } from '../utils';
const Exit: FC<{ didError?: boolean }> = ({ didError }) => isDone ? null : <Box marginTop={1} display="flex">
<Text color={didError ? "#FF1639" : "#FFBE2D"}>[abort]</Text>
const Exit: FC<{ didError?: boolean }> = ({ didError }) =>
isDone ? null : (
<Box marginTop={1} display="flex">
<Text color={didError ? '#FF1639' : '#FFBE2D'}>[abort]</Text>
<Text> astro cancelled</Text>
</Box>
</Box>
);
export default Exit;

View file

@ -8,10 +8,14 @@ const Finalize: FC<{ context: any }> = ({ context: { use, projectName } }) => {
process.exit(0);
}, []);
return <>
return (
<>
<Box display="flex">
<Text color="#17C083">{'[ yes ]'}</Text>
<Text> Project initialized at <Text color="#3894FF">./{projectName}</Text></Text>
<Text>
{' '}
Project initialized at <Text color="#3894FF">./{projectName}</Text>
</Text>
</Box>
<Box display="flex" marginY={1}>
<Text dimColor>{'[ tip ]'}</Text>
@ -21,7 +25,8 @@ const Finalize: FC<{ context: any }> = ({ context: { use, projectName } }) => {
<Text color="#3894FF">{use} start</Text>
</Box>
</Box>
</>;
</>
);
};
export default Finalize;

View file

@ -3,18 +3,26 @@ import { Box, Text } from 'ink';
const getMessage = ({ projectName, template }) => {
switch (true) {
case !projectName: return <Text dimColor>Gathering mission details</Text>;
case !template: return <Text dimColor>Optimizing navigational system</Text>;
default: return <Text color="black" backgroundColor="white"> {projectName} </Text>
case !projectName:
return <Text dimColor>Gathering mission details</Text>;
case !template:
return <Text dimColor>Optimizing navigational system</Text>;
default:
return (
<Text color="black" backgroundColor="white">
{' '}
{projectName}{' '}
</Text>
);
}
}
};
const Header: React.FC<{ context: any }> = ({ context }) => (
<Box width={48} display="flex" marginY={1}>
<Text backgroundColor="#882DE7" color="white">{' astro '}</Text>
<Box marginLeft={1}>
{getMessage(context)}
<Text backgroundColor="#882DE7" color="white">
{' astro '}
</Text>
<Box marginLeft={1}>{getMessage(context)}</Box>
</Box>
</Box>
)
);
export default Header;

View file

@ -2,37 +2,48 @@ import React, { FC } from 'react';
import { Box, Text } from 'ink';
import { ARGS, ARG } from '../config';
const Type: FC<{ type: any, enum?: string[] }> = ({ type, enum: e }) => {
const Type: FC<{ type: any; enum?: string[] }> = ({ type, enum: e }) => {
if (type === Boolean) {
return <>
return (
<>
<Text color="#3894FF">true</Text>
<Text dimColor>|</Text>
<Text color="#3894FF">false</Text>
</>
);
}
if (e?.length > 0) {
return <>
{e.map((item, i, { length: len}) => {
return (
<>
{e.map((item, i, { length: len }) => {
if (i !== len - 1) {
return <Box key={item}>
return (
<Box key={item}>
<Text color="#17C083">{item}</Text>
<Text dimColor>|</Text>
</Box>
);
}
return <Text color="#17C083" key={item}>{item}</Text>
return (
<Text color="#17C083" key={item}>
{item}
</Text>
);
})}
</>
);
}
return <Text color="#3894FF">string</Text>;
}
};
const Command: FC<{ name: string, info: ARG }> = ({ name, info: { alias, description, type, enum: e } }) => {
const Command: FC<{ name: string; info: ARG }> = ({ name, info: { alias, description, type, enum: e } }) => {
return (
<Box display="flex" alignItems="flex-start">
<Box width={24} display="flex" flexGrow={0}>
<Text color="whiteBright">--{name}</Text>{alias && <Text dimColor> -{alias}</Text>}
<Text color="whiteBright">--{name}</Text>
{alias && <Text dimColor> -{alias}</Text>}
</Box>
<Box width={24}>
<Type type={type} enum={e} />
@ -42,21 +53,28 @@ const Command: FC<{ name: string, info: ARG }> = ({ name, info: { alias, descrip
</Box>
</Box>
);
}
};
const Help: FC<{ context: any }> = ({ context: { templates }}) => {
const Help: FC<{ context: any }> = ({ context: { templates } }) => {
return (
<>
<Box width={48} display="flex" marginY={1}>
<Text backgroundColor="#882DE7" color="white">{' astro '}</Text>
<Text backgroundColor="#882DE7" color="white">
{' astro '}
</Text>
<Box marginLeft={1}>
<Text color="black" backgroundColor="white"> help </Text>
<Text color="black" backgroundColor="white">
{' '}
help{' '}
</Text>
</Box>
</Box>
<Box marginBottom={1} marginLeft={2} display="flex" flexDirection="column">
{Object.entries(ARGS).map(([name, info]) => <Command key={name} name={name} info={name === 'template' ? { ...info, enum: templates.map(({ value }) => value) } : info} /> )}
{Object.entries(ARGS).map(([name, info]) => (
<Command key={name} name={name} info={name === 'template' ? { ...info, enum: templates.map(({ value }) => value) } : info} />
))}
</Box>
</>
)
);
};
export default Help;

View file

@ -4,16 +4,20 @@ import Spacer from './Spacer';
import Spinner from './Spinner';
const Install: FC<{ context: any }> = ({ context: { use } }) => {
return <>
return (
<>
<Box display="flex">
<Spinner/>
<Spinner />
<Text> Initiating launch sequence...</Text>
</Box>
<Box>
<Spacer />
<Text color="white" dimColor>(aka running <Text color="#17C083">{use === 'npm' ? 'npm install' : 'yarn'}</Text>)</Text>
<Text color="white" dimColor>
(aka running <Text color="#17C083">{use === 'npm' ? 'npm install' : 'yarn'}</Text>)
</Text>
</Box>
</>;
</>
);
};
export default Install;

View file

@ -9,7 +9,8 @@ const ProjectName: FC<{ onSubmit: (value: string) => void }> = ({ onSubmit }) =>
const [value, setValue] = React.useState('');
const handleSubmit = (v: string) => onSubmit(v);
return <>
return (
<>
<Box display="flex">
<Text color="#17C083">{'[query]'}</Text>
<Text> What is your project name?</Text>
@ -18,7 +19,8 @@ const ProjectName: FC<{ onSubmit: (value: string) => void }> = ({ onSubmit }) =>
<Spacer />
<Input value={value} onChange={setValue} onSubmit={handleSubmit} placeholder="my-project" />
</Box>
</>;
</>
);
};
export default ProjectName;

View file

@ -9,24 +9,24 @@ interface Props {
label: string;
description?: string;
}
const Indicator: FC<Props> = ({ isSelected }) => isSelected ? <Text color="#3894FF">[ </Text> : <Text> </Text>
const Item: FC<Props> = ({isSelected = false, label, description }) => (
const Indicator: FC<Props> = ({ isSelected }) => (isSelected ? <Text color="#3894FF">[ </Text> : <Text> </Text>);
const Item: FC<Props> = ({ isSelected = false, label, description }) => (
<Box display="flex">
<Text color={isSelected ? '#3894FF' : 'white'} dimColor={!isSelected}>{label}</Text>
<Text color={isSelected ? '#3894FF' : 'white'} dimColor={!isSelected}>
{label}
</Text>
{isSelected && description && typeof description === 'string' && <Text> {description}</Text>}
{isSelected && description && typeof description !== 'string' && <Box marginLeft={1}>{description}</Box>}
</Box>
);
interface SelectProps {
items: { value: string|number|boolean, label: string, description?: any }[]
onSelect(value: string|number|boolean): void;
items: { value: string | number | boolean; label: string; description?: any }[];
onSelect(value: string | number | boolean): void;
}
const CustomSelect: FC<SelectProps> = ({ items, onSelect }) => {
const handleSelect = ({ value }) => onSelect(value);
return (
<Select indicatorComponent={Indicator} itemComponent={Item} items={items} onSelect={handleSelect} />
)
}
return <Select indicatorComponent={Indicator} itemComponent={Item} items={items} onSelect={handleSelect} />;
};
export default CustomSelect;

View file

@ -1,5 +1,5 @@
import React, { FC } from 'react';
import { Box } from 'ink';
const Spacer: FC<{ width?: number }> = ({ width = 8 }) => <Box width={width} />
const Spacer: FC<{ width?: number }> = ({ width = 8 }) => <Box width={width} />;
export default Spacer;

View file

@ -6,14 +6,14 @@ const Spinner: FC<{ type?: keyof typeof spinners }> = ({ type = 'countdown' }) =
const [i, setI] = useState(0);
useEffect(() => {
const _ = setInterval(() => {
setI(v => (v < frames.length - 1) ? v + 1 : 0)
}, interval)
setI((v) => (v < frames.length - 1 ? v + 1 : 0));
}, interval);
return () => clearInterval(_);
}, [])
}, []);
return frames[i]
}
return frames[i];
};
const spinners = {
countdown: {
@ -35,73 +35,73 @@ const spinners = {
<Text backgroundColor="#882DE7">{' '}</Text>
</Box>,
<Box display="flex">
<Text backgroundColor="#6858F1">{' '}</Text>
<Text backgroundColor="#6858F1"> </Text>
<Text backgroundColor="#882DE7">{' '}</Text>
</Box>,
<Box display="flex">
<Text backgroundColor="#5076F9">{' '}</Text>
<Text backgroundColor="#6858F1">{' '}</Text>
<Text backgroundColor="#5076F9"> </Text>
<Text backgroundColor="#6858F1"> </Text>
<Text backgroundColor="#882DE7">{' '}</Text>
</Box>,
<Box display="flex">
<Text backgroundColor="#3894FF">{' '}</Text>
<Text backgroundColor="#5076F9">{' '}</Text>
<Text backgroundColor="#6858F1">{' '}</Text>
<Text backgroundColor="#3894FF"> </Text>
<Text backgroundColor="#5076F9"> </Text>
<Text backgroundColor="#6858F1"> </Text>
<Text backgroundColor="#882DE7">{' '}</Text>
</Box>,
<Box display="flex">
<Text backgroundColor="#2CA5D2">{' '}</Text>
<Text backgroundColor="#3894FF">{' '}</Text>
<Text backgroundColor="#5076F9">{' '}</Text>
<Text backgroundColor="#6858F1">{' '}</Text>
<Text backgroundColor="#2CA5D2"> </Text>
<Text backgroundColor="#3894FF"> </Text>
<Text backgroundColor="#5076F9"> </Text>
<Text backgroundColor="#6858F1"> </Text>
<Text backgroundColor="#882DE7">{' '}</Text>
</Box>,
<Box display="flex">
<Text backgroundColor="#23B1AF">{' '}</Text>
<Text backgroundColor="#2CA5D2">{' '}</Text>
<Text backgroundColor="#3894FF">{' '}</Text>
<Text backgroundColor="#5076F9">{' '}</Text>
<Text backgroundColor="#6858F1">{' '}</Text>
<Text backgroundColor="#23B1AF"> </Text>
<Text backgroundColor="#2CA5D2"> </Text>
<Text backgroundColor="#3894FF"> </Text>
<Text backgroundColor="#5076F9"> </Text>
<Text backgroundColor="#6858F1"> </Text>
<Text backgroundColor="#882DE7">{' '}</Text>
</Box>,
<Box display="flex">
<Text backgroundColor="#17C083">{' '}</Text>
<Text backgroundColor="#23B1AF">{' '}</Text>
<Text backgroundColor="#2CA5D2">{' '}</Text>
<Text backgroundColor="#3894FF">{' '}</Text>
<Text backgroundColor="#5076F9">{' '}</Text>
<Text backgroundColor="#6858F1">{' '}</Text>
<Text backgroundColor="#882DE7">{' '}</Text>
<Text backgroundColor="#17C083"> </Text>
<Text backgroundColor="#23B1AF"> </Text>
<Text backgroundColor="#2CA5D2"> </Text>
<Text backgroundColor="#3894FF"> </Text>
<Text backgroundColor="#5076F9"> </Text>
<Text backgroundColor="#6858F1"> </Text>
<Text backgroundColor="#882DE7"> </Text>
</Box>,
<Box display="flex">
<Text backgroundColor="#17C083">{' '}</Text>
<Text backgroundColor="#23B1AF">{' '}</Text>
<Text backgroundColor="#2CA5D2">{' '}</Text>
<Text backgroundColor="#3894FF">{' '}</Text>
<Text backgroundColor="#5076F9">{' '}</Text>
<Text backgroundColor="#6858F1">{' '}</Text>
<Text backgroundColor="#23B1AF"> </Text>
<Text backgroundColor="#2CA5D2"> </Text>
<Text backgroundColor="#3894FF"> </Text>
<Text backgroundColor="#5076F9"> </Text>
<Text backgroundColor="#6858F1"> </Text>
</Box>,
<Box display="flex">
<Text backgroundColor="#17C083">{' '}</Text>
<Text backgroundColor="#23B1AF">{' '}</Text>
<Text backgroundColor="#2CA5D2">{' '}</Text>
<Text backgroundColor="#3894FF">{' '}</Text>
<Text backgroundColor="#5076F9">{' '}</Text>
<Text backgroundColor="#23B1AF"> </Text>
<Text backgroundColor="#2CA5D2"> </Text>
<Text backgroundColor="#3894FF"> </Text>
<Text backgroundColor="#5076F9"> </Text>
</Box>,
<Box display="flex">
<Text backgroundColor="#17C083">{' '}</Text>
<Text backgroundColor="#23B1AF">{' '}</Text>
<Text backgroundColor="#2CA5D2">{' '}</Text>
<Text backgroundColor="#3894FF">{' '}</Text>
<Text backgroundColor="#23B1AF"> </Text>
<Text backgroundColor="#2CA5D2"> </Text>
<Text backgroundColor="#3894FF"> </Text>
</Box>,
<Box display="flex">
<Text backgroundColor="#17C083">{' '}</Text>
<Text backgroundColor="#23B1AF">{' '}</Text>
<Text backgroundColor="#2CA5D2">{' '}</Text>
<Text backgroundColor="#23B1AF"> </Text>
<Text backgroundColor="#2CA5D2"> </Text>
</Box>,
<Box display="flex">
<Text backgroundColor="#17C083">{' '}</Text>
<Text backgroundColor="#23B1AF">{' '}</Text>
<Text backgroundColor="#23B1AF"> </Text>
</Box>,
<Box display="flex">
<Text backgroundColor="#17C083">{' '}</Text>
@ -122,79 +122,79 @@ const spinners = {
<Text backgroundColor="#17C083">{' '}</Text>
</Box>,
<Box display="flex">
<Text backgroundColor="#23B1AF">{' '}</Text>
<Text backgroundColor="#23B1AF"> </Text>
<Text backgroundColor="#17C083">{' '}</Text>
</Box>,
<Box display="flex">
<Text backgroundColor="#2CA5D2">{' '}</Text>
<Text backgroundColor="#23B1AF">{' '}</Text>
<Text backgroundColor="#2CA5D2"> </Text>
<Text backgroundColor="#23B1AF"> </Text>
<Text backgroundColor="#17C083">{' '}</Text>
</Box>,
<Box display="flex">
<Text backgroundColor="#3894FF">{' '}</Text>
<Text backgroundColor="#2CA5D2">{' '}</Text>
<Text backgroundColor="#23B1AF">{' '}</Text>
<Text backgroundColor="#3894FF"> </Text>
<Text backgroundColor="#2CA5D2"> </Text>
<Text backgroundColor="#23B1AF"> </Text>
<Text backgroundColor="#17C083">{' '}</Text>
</Box>,
<Box display="flex">
<Text backgroundColor="#5076F9">{' '}</Text>
<Text backgroundColor="#3894FF">{' '}</Text>
<Text backgroundColor="#2CA5D2">{' '}</Text>
<Text backgroundColor="#23B1AF">{' '}</Text>
<Text backgroundColor="#5076F9"> </Text>
<Text backgroundColor="#3894FF"> </Text>
<Text backgroundColor="#2CA5D2"> </Text>
<Text backgroundColor="#23B1AF"> </Text>
<Text backgroundColor="#17C083">{' '}</Text>
</Box>,
<Box display="flex">
<Text backgroundColor="#6858F1">{' '}</Text>
<Text backgroundColor="#5076F9">{' '}</Text>
<Text backgroundColor="#3894FF">{' '}</Text>
<Text backgroundColor="#2CA5D2">{' '}</Text>
<Text backgroundColor="#23B1AF">{' '}</Text>
<Text backgroundColor="#6858F1"> </Text>
<Text backgroundColor="#5076F9"> </Text>
<Text backgroundColor="#3894FF"> </Text>
<Text backgroundColor="#2CA5D2"> </Text>
<Text backgroundColor="#23B1AF"> </Text>
<Text backgroundColor="#17C083">{' '}</Text>
</Box>,
<Box display="flex">
<Text backgroundColor="#882DE7"> </Text>
<Text backgroundColor="#6858F1"> </Text>
<Text backgroundColor="#5076F9"> </Text>
<Text backgroundColor="#3894FF"> </Text>
<Text backgroundColor="#2CA5D2"> </Text>
<Text backgroundColor="#23B1AF"> </Text>
<Text backgroundColor="#17C083"> </Text>
</Box>,
<Box display="flex">
<Text backgroundColor="#882DE7">{' '}</Text>
<Text backgroundColor="#6858F1">{' '}</Text>
<Text backgroundColor="#5076F9">{' '}</Text>
<Text backgroundColor="#3894FF">{' '}</Text>
<Text backgroundColor="#2CA5D2">{' '}</Text>
<Text backgroundColor="#23B1AF">{' '}</Text>
<Text backgroundColor="#17C083">{' '}</Text>
<Text backgroundColor="#6858F1"> </Text>
<Text backgroundColor="#5076F9"> </Text>
<Text backgroundColor="#3894FF"> </Text>
<Text backgroundColor="#2CA5D2"> </Text>
<Text backgroundColor="#23B1AF"> </Text>
</Box>,
<Box display="flex">
<Text backgroundColor="#882DE7">{' '}</Text>
<Text backgroundColor="#6858F1">{' '}</Text>
<Text backgroundColor="#5076F9">{' '}</Text>
<Text backgroundColor="#3894FF">{' '}</Text>
<Text backgroundColor="#2CA5D2">{' '}</Text>
<Text backgroundColor="#23B1AF">{' '}</Text>
<Text backgroundColor="#6858F1"> </Text>
<Text backgroundColor="#5076F9"> </Text>
<Text backgroundColor="#3894FF"> </Text>
<Text backgroundColor="#2CA5D2"> </Text>
</Box>,
<Box display="flex">
<Text backgroundColor="#882DE7">{' '}</Text>
<Text backgroundColor="#6858F1">{' '}</Text>
<Text backgroundColor="#5076F9">{' '}</Text>
<Text backgroundColor="#3894FF">{' '}</Text>
<Text backgroundColor="#2CA5D2">{' '}</Text>
<Text backgroundColor="#6858F1"> </Text>
<Text backgroundColor="#5076F9"> </Text>
<Text backgroundColor="#3894FF"> </Text>
</Box>,
<Box display="flex">
<Text backgroundColor="#882DE7">{' '}</Text>
<Text backgroundColor="#6858F1">{' '}</Text>
<Text backgroundColor="#5076F9">{' '}</Text>
<Text backgroundColor="#3894FF">{' '}</Text>
<Text backgroundColor="#6858F1"> </Text>
<Text backgroundColor="#5076F9"> </Text>
</Box>,
<Box display="flex">
<Text backgroundColor="#882DE7">{' '}</Text>
<Text backgroundColor="#6858F1">{' '}</Text>
<Text backgroundColor="#5076F9">{' '}</Text>
</Box>,
<Box display="flex">
<Text backgroundColor="#882DE7">{' '}</Text>
<Text backgroundColor="#6858F1">{' '}</Text>
<Text backgroundColor="#6858F1"> </Text>
</Box>,
<Box display="flex">
<Text backgroundColor="#882DE7">{' '}</Text>
</Box>,
]
}
}
],
},
};
export default Spinner;

View file

@ -3,7 +3,7 @@ import { Box, Text } from 'ink';
import Spacer from './Spacer';
import Select from './Select';
const Template: FC<{ context: any, onSubmit: (value: string) => void }> = ({ context: { templates }, onSubmit }) => {
const Template: FC<{ context: any; onSubmit: (value: string) => void }> = ({ context: { templates }, onSubmit }) => {
const items = templates.map(({ title: label, ...rest }) => ({ ...rest, label }));
return (

View file

@ -8,35 +8,35 @@ export interface ARG {
}
export const ARGS: Record<string, ARG> = {
'template': {
template: {
type: String,
description: 'specifies template to use'
description: 'specifies template to use',
},
'use': {
use: {
type: String,
enum: ['npm', 'yarn'],
description: 'specifies package manager to use'
description: 'specifies package manager to use',
},
'run': {
run: {
type: Boolean,
description: 'should dependencies be installed automatically?'
description: 'should dependencies be installed automatically?',
},
'force': {
force: {
type: Boolean,
alias: 'f',
description: 'should existing files be overwritten?'
description: 'should existing files be overwritten?',
},
'version': {
version: {
type: Boolean,
alias: 'v',
description: 'prints current version'
description: 'prints current version',
},
'help': {
help: {
type: Boolean,
alias: 'h',
description: 'prints this message'
}
}
description: 'prints this message',
},
};
export const args = Object.entries(ARGS).reduce((acc, [name, info]) => {
const key = `--${name}`;
@ -45,5 +45,5 @@ export const args = Object.entries(ARGS).reduce((acc, [name, info]) => {
if (info.alias) {
spec[`-${info.alias}`] = key;
}
return spec
return spec;
}, {} as arg.Spec);

View file

@ -3,7 +3,7 @@ import React from 'react';
import App from './components/App';
import Version from './components/Version';
import Exit from './components/Exit';
import {render} from 'ink';
import { render } from 'ink';
import { getTemplates, addProcessListeners } from './utils';
import { args as argsConfig } from './config';
import arg from 'arg';
@ -18,11 +18,11 @@ export default async function createAstro() {
}
const templates = await getTemplates();
if (args['--help']) {
return render(<Help context={{ templates }} />)
return render(<Help context={{ templates }} />);
}
const pkgManager = /yarn/.test(process.env.npm_execpath) ? 'yarn' : 'npm';
const use = (args['--use'] ?? pkgManager) as 'npm'|'yarn';
const use = (args['--use'] ?? pkgManager) as 'npm' | 'yarn';
const template = args['--template'];
const force = args['--force'];
const run = args['--run'] ?? true;
@ -32,15 +32,15 @@ export default async function createAstro() {
const onError = () => {
if (app) app.clear();
render(<Exit didError />);
}
};
const onExit = () => {
if (app) app.clear();
render(<Exit />);
}
};
addProcessListeners([
['uncaughtException', onError],
['exit', onExit],
['SIGINT', onExit],
['SIGTERM', onExit],
])
]);
}

View file

@ -21,10 +21,8 @@ Inside of your Astro project, you'll see the following folders and files:
Astro looks for `.astro` or `.md.astro` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
Any static assets, like images, can be placed in the `public/` directory.
## 👀 Want to learn more?

View file

@ -6,8 +6,8 @@ import decompress from 'decompress';
const listeners = new Map();
export async function addProcessListeners(handlers: [NodeJS.Signals|string, NodeJS.SignalsListener][]) {
for (const [event,handler] of handlers) {
export async function addProcessListeners(handlers: [NodeJS.Signals | string, NodeJS.SignalsListener][]) {
for (const [event, handler] of handlers) {
listeners.set(event, handler);
process.once(event as NodeJS.Signals, handler);
}
@ -23,16 +23,18 @@ export async function cancelProcessListeners() {
export async function getTemplates() {
const templatesRoot = fileURLToPath(new URL('./templates', import.meta.url));
const templateFiles = await fs.readdir(templatesRoot, 'utf8');
const templates = templateFiles.filter(t => t.endsWith('.tgz'));
const metafile = templateFiles.find(t => t.endsWith('meta.json'));
const templates = templateFiles.filter((t) => t.endsWith('.tgz'));
const metafile = templateFiles.find((t) => t.endsWith('meta.json'));
const meta = await fs.readFile(resolve(templatesRoot, metafile)).then(r => JSON.parse(r.toString()));
const meta = await fs.readFile(resolve(templatesRoot, metafile)).then((r) => JSON.parse(r.toString()));
return templates.map(template => {
return templates
.map((template) => {
const value = basename(template, '.tgz');
if (meta[value]) return { ...meta[value], value };
return { value };
}).sort((a, b) => {
})
.sort((a, b) => {
const aRank = a.rank ?? 0;
const bRank = b.rank ?? 0;
if (aRank > bRank) return -1;
@ -49,16 +51,17 @@ export async function rewriteFiles(projectName: string) {
const tasks = [];
tasks.push(fs.rename(resolve(dest, '_gitignore'), resolve(dest, '.gitignore')));
tasks.push(
fs.readFile(resolve(dest, 'package.json'))
.then(res => JSON.parse(res.toString()))
.then(json => JSON.stringify({ ...json, name: getValidPackageName(projectName) }, null, 2))
.then(res => fs.writeFile(resolve(dest, 'package.json'), res))
fs
.readFile(resolve(dest, 'package.json'))
.then((res) => JSON.parse(res.toString()))
.then((json) => JSON.stringify({ ...json, name: getValidPackageName(projectName) }, null, 2))
.then((res) => fs.writeFile(resolve(dest, 'package.json'), res))
);
return Promise.all(tasks);
}
export async function prepareTemplate(use: 'npm'|'yarn', name: string, dest: string) {
export async function prepareTemplate(use: 'npm' | 'yarn', name: string, dest: string) {
const projectName = dest;
dest = resolve(dest);
const template = fileURLToPath(new URL(`./templates/${name}.tgz`, import.meta.url));
@ -81,10 +84,10 @@ export function cleanup(didError = false) {
}
export function killChildren() {
childrenProcesses.forEach(p => p.kill('SIGINT'));
childrenProcesses.forEach((p) => p.kill('SIGINT'));
}
export function run(pkgManager: 'npm'|'yarn', command: string, projectPath: string, stdio: any = 'ignore'): Promise<void> {
export function run(pkgManager: 'npm' | 'yarn', command: string, projectPath: string, stdio: any = 'ignore'): Promise<void> {
return new Promise((resolve, reject) => {
const p = spawn(pkgManager, command ? [command] : [], {
shell: true,
@ -118,24 +121,24 @@ export function isEmpty(path) {
export function emptyDir(dir) {
dir = resolve(dir);
if (!existsSync(dir)) {
return
return;
}
for (const file of readdirSync(dir)) {
const abs = resolve(dir, file)
const abs = resolve(dir, file);
if (lstatSync(abs).isDirectory()) {
emptyDir(abs)
rmdirSync(abs)
emptyDir(abs);
rmdirSync(abs);
} else {
unlinkSync(abs)
unlinkSync(abs);
}
}
}
export function getValidPackageName(projectName: string) {
const packageNameRegExp = /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/
const packageNameRegExp = /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
if (packageNameRegExp.test(projectName)) {
return projectName
return projectName;
}
return projectName

View file

@ -5,56 +5,57 @@ import { promises as fs } from 'fs';
const convertMessage = ({ message, start, end, filename, frame }) => ({
text: message,
location: start && end && {
location: start &&
end && {
file: filename,
line: start.line,
column: start.column,
length: start.line === end.line ? end.column - start.column : 0,
lineText: frame,
},
})
});
const handleLoad = async (args, generate) => {
const { path } = args;
const source = await fs.readFile(path, 'utf8');
const filename = relative(process.cwd(), path)
const filename = relative(process.cwd(), path);
try {
let compileOptions = { css: false, generate, hydratable: true };
let { js, warnings } = compile(source, { ...compileOptions, filename })
let contents = js.code + `\n//# sourceMappingURL=` + js.map.toUrl()
let { js, warnings } = compile(source, { ...compileOptions, filename });
let contents = js.code + `\n//# sourceMappingURL=` + js.map.toUrl();
return { loader: 'js', contents, resolveDir: dirname(path), warnings: warnings.map(w => convertMessage(w)) };
return { loader: 'js', contents, resolveDir: dirname(path), warnings: warnings.map((w) => convertMessage(w)) };
} catch (e) {
return { errors: [convertMessage(e)] }
return { errors: [convertMessage(e)] };
}
}
};
export default function sveltePlugin() {
return {
name: 'svelte-esbuild',
setup(build) {
build.onResolve({ filter: /\.svelte$/ }, args => {
build.onResolve({ filter: /\.svelte$/ }, (args) => {
let path = args.path.replace(/\.(?:client|server)/, '');
path = isAbsolute(path) ? path : join(args.resolveDir, path)
path = isAbsolute(path) ? path : join(args.resolveDir, path);
if (/\.client\.svelte$/.test(args.path)) {
return {
path,
namespace: 'svelte:client',
}
};
}
if (/\.server\.svelte$/.test(args.path)) {
return {
path,
namespace: 'svelte:server',
}
};
}
});
build.onLoad({ filter: /.*/, namespace: 'svelte:client' }, (args) => handleLoad(args, 'dom'))
build.onLoad({ filter: /.*/, namespace: 'svelte:server' }, (args) => handleLoad(args, 'ssr'))
build.onLoad({ filter: /.*/, namespace: 'svelte:client' }, (args) => handleLoad(args, 'dom'));
build.onLoad({ filter: /.*/, namespace: 'svelte:server' }, (args) => handleLoad(args, 'ssr'));
},
}
};
}

View file

@ -42,7 +42,7 @@ module.exports.parsers = {
return node.end;
},
astFormat: 'astro-expression',
}
},
};
const findExpressionsInAST = (node, collect = []) => {
@ -50,24 +50,24 @@ const findExpressionsInAST = (node, collect = []) => {
return collect.concat(node);
}
if (node.children) {
collect.push(...[].concat(...node.children.map(child => findExpressionsInAST(child))));
collect.push(...[].concat(...node.children.map((child) => findExpressionsInAST(child))));
}
return collect;
}
};
const formatExpression = ({ expression: { codeChunks, children }}, text, options) => {
const formatExpression = ({ expression: { codeChunks, children } }, text, options) => {
if (children.length === 0) {
const codeStart = codeChunks[0]; // If no children, there should only exist a single chunk.
if (codeStart && [`'`, `"`].includes(codeStart[0])) {
return `<script $ lang="ts">${codeChunks.join('')}</script>`
return `<script $ lang="ts">${codeChunks.join('')}</script>`;
}
return `{${codeChunks.join('')}}`;
}
return `<script $ lang="ts">${text}</script>`;
}
};
const isAstroScript = (node) => node.type === 'concat' && node.parts[0] === '<script' && node.parts[1].type === 'indent' && node.parts[1].contents.parts.find(v => v === '$');
const isAstroScript = (node) => node.type === 'concat' && node.parts[0] === '<script' && node.parts[1].type === 'indent' && node.parts[1].contents.parts.find((v) => v === '$');
const walkDoc = (doc) => {
let inAstroScript = false;
@ -77,38 +77,38 @@ const walkDoc = (doc) => {
inAstroScript = true;
parent.contents = { type: 'concat', parts: ['{'] };
}
return node.parts.map(part => recurse(part, { parent: node }));
return node.parts.map((part) => recurse(part, { parent: node }));
}
if (inAstroScript) {
if (node.type === 'break-parent') {
parent.parts = parent.parts.filter(part => !['break-parent', 'line'].includes(part.type));
parent.parts = parent.parts.filter((part) => !['break-parent', 'line'].includes(part.type));
}
if (node.type === 'indent') {
parent.parts = parent.parts.map(part => {
parent.parts = parent.parts.map((part) => {
if (part.type !== 'indent') return part;
return {
type: 'concat',
parts: [part.contents]
}
})
parts: [part.contents],
};
});
}
if (typeof node === 'string' && node.endsWith(';')) {
parent.parts = parent.parts.map(part => {
parent.parts = parent.parts.map((part) => {
if (typeof part === 'string' && part.endsWith(';')) return part.slice(0, -1);
return part;
});
}
if (node === '</script>') {
parent.parts = parent.parts.map(part => part === '</script>' ? '}' : part);
parent.parts = parent.parts.map((part) => (part === '</script>' ? '}' : part));
inAstroScript = false;
}
}
if (['group', 'indent'].includes(node.type)) {
return recurse(node.contents, { parent: node });
}
}
};
recurse(doc, { parent: null });
}
};
/** @type {Record<string, import('prettier').Printer>} */
module.exports.printers = {
@ -129,18 +129,20 @@ module.exports.printers = {
if (node.type === 'Fragment' && node.isRoot) {
const expressions = findExpressionsInAST(node);
if (expressions.length > 0) {
const parts = [].concat(...expressions.map((expr, i, all) => {
const parts = [].concat(
...expressions.map((expr, i, all) => {
const prev = all[i - 1];
const start = node.text.slice((prev?.end ?? node.start) - node.start, expr.start - node.start);
const exprText = formatExpression(expr, node.text.slice(expr.start - node.start + 1, expr.end - node.start - 1), options);
if (i === all.length - 1) {
const end = node.text.slice(expr.end - node.start);
return [start, exprText, end]
return [start, exprText, end];
}
return [start, exprText]
}));
return [start, exprText];
})
);
const html = parts.join('\n');
const doc = textToDoc(html, { parser: 'html' });
walkDoc(doc);

View file

@ -2,10 +2,10 @@ import { suite } from 'uvu';
import * as assert from 'uvu/assert';
import { format } from './test-utils.js';
import { promises as fs } from 'fs';
import { fileURLToPath } from 'url'
import { fileURLToPath } from 'url';
const Prettier = suite('Prettier formatting');
const readFile = (path) => fs.readFile(fileURLToPath(new URL(`./fixtures${path}`, import.meta.url))).then(res => res.toString())
const readFile = (path) => fs.readFile(fileURLToPath(new URL(`./fixtures${path}`, import.meta.url))).then((res) => res.toString());
/**
* Utility to get `[src, out]` files

View file

@ -1,6 +1,6 @@
{
"comments": {
"blockComment": [ "<!--", "-->" ]
"blockComment": ["<!--", "-->"]
},
"brackets": [
["---", "---"],
@ -10,20 +10,20 @@
["(", ")"]
],
"autoClosingPairs": [
{ "open": "{", "close": "}"},
{ "open": "[", "close": "]"},
{ "open": "{", "close": "}" },
{ "open": "[", "close": "]" },
{ "open": "(", "close": ")" },
{ "open": "'", "close": "'" },
{ "open": "\"", "close": "\"" },
{ "open": "<!--", "close": "-->", "notIn": [ "comment", "string" ]},
{ "open": "<!--", "close": "-->", "notIn": ["comment", "string"] },
{ "open": "/**", "close": " */", "notIn": ["string"] }
],
"autoCloseBefore": ";:.,=}])>` \n\t",
"surroundingPairs": [
{ "open": "'", "close": "'" },
{ "open": "\"", "close": "\"" },
{ "open": "{", "close": "}"},
{ "open": "[", "close": "]"},
{ "open": "{", "close": "}" },
{ "open": "[", "close": "]" },
{ "open": "(", "close": ")" },
{ "open": "<", "close": ">" }
],

View file

@ -17,11 +17,7 @@ export function activateTagClosing(
configName: string
): Disposable {
const disposables: Disposable[] = [];
workspace.onDidChangeTextDocument(
(event) => onDidChangeTextDocument(event.document, event.contentChanges),
null,
disposables
);
workspace.onDidChangeTextDocument((event) => onDidChangeTextDocument(event.document, event.contentChanges), null, disposables);
let isEnabled = false;
updateEnabledState();
@ -47,10 +43,7 @@ export function activateTagClosing(
}
/** Handle text document changes */
function onDidChangeTextDocument(
document: TextDocument,
changes: readonly TextDocumentContentChangeEvent[]
) {
function onDidChangeTextDocument(document: TextDocument, changes: readonly TextDocumentContentChangeEvent[]) {
if (!isEnabled) {
return;
}
@ -63,22 +56,13 @@ export function activateTagClosing(
}
const lastChange = changes[changes.length - 1];
const lastCharacter = lastChange.text[lastChange.text.length - 1];
if (
('range' in lastChange && (lastChange.rangeLength ?? 0) > 0) ||
(lastCharacter !== '>' && lastCharacter !== '/')
) {
if (('range' in lastChange && (lastChange.rangeLength ?? 0) > 0) || (lastCharacter !== '>' && lastCharacter !== '/')) {
return;
}
const rangeStart =
'range' in lastChange
? lastChange.range.start
: new Position(0, document.getText().length);
const rangeStart = 'range' in lastChange ? lastChange.range.start : new Position(0, document.getText().length);
const version = document.version;
timeout = setTimeout(() => {
const position = new Position(
rangeStart.line,
rangeStart.character + lastChange.text.length
);
const position = new Position(rangeStart.line, rangeStart.character + lastChange.text.length);
tagProvider(document, position).then((text) => {
if (text && isEnabled) {
const activeEditor = window.activeTextEditor;
@ -86,10 +70,7 @@ export function activateTagClosing(
const activeDocument = activeEditor.document;
if (document === activeDocument && activeDocument.version === version) {
const selections = activeEditor.selections;
if (
selections.length &&
selections.some((s) => s.active.isEqual(position))
) {
if (selections.length && selections.some((s) => s.active.isEqual(position))) {
activeEditor.insertSnippet(
new SnippetString(text),
selections.map((s) => s.active)

View file

@ -2,11 +2,9 @@
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src",
"rootDir": "src"
},
"include": ["src"],
"exclude": ["node_modules"],
"references": [
{ "path": "../server" }
]
"references": [{ "path": "../server" }]
}

View file

@ -7,7 +7,6 @@ import { parseHtml } from './parseHtml';
import { parseAstro, AstroDocument } from './parseAstro';
export class Document implements TextDocument {
private content: string;
languageId = 'astro';
@ -43,7 +42,7 @@ export class Document implements TextDocument {
}
getText(): string {
return this.content
return this.content;
}
/**
@ -89,10 +88,7 @@ export class Document implements TextDocument {
}
const lineOffset = lineOffsets[position.line];
const nextLineOffset =
position.line + 1 < lineOffsets.length
? lineOffsets[position.line + 1]
: this.getTextLength();
const nextLineOffset = position.line + 1 < lineOffsets.length ? lineOffsets[position.line + 1] : this.getTextLength();
return clamp(nextLineOffset, lineOffset, lineOffset + position.character);
}
@ -147,7 +143,6 @@ export class Document implements TextDocument {
return this.uri;
}
get lines(): string[] {
return this.getText().split(/\r?\n/);
}
@ -155,5 +150,4 @@ export class Document implements TextDocument {
get lineCount(): number {
return this.lines.length;
}
}

View file

@ -1,8 +1,5 @@
import { EventEmitter } from 'events';
import {
TextDocumentContentChangeEvent,
TextDocumentItem
} from 'vscode-languageserver';
import { TextDocumentContentChangeEvent, TextDocumentItem } from 'vscode-languageserver';
import { Document } from './Document';
import { normalizeUri } from '../../utils';
@ -15,9 +12,7 @@ export class DocumentManager {
private locked = new Set<string>();
private deleteCandidates = new Set<string>();
constructor(
private createDocument: (textDocument: { uri: string, text: string }) => Document
) {}
constructor(private createDocument: (textDocument: { uri: string; text: string }) => Document) {}
get(uri: string) {
return this.documents.get(normalizeUri(uri));
@ -59,10 +54,7 @@ export class DocumentManager {
this.openedInClient.delete(uri);
}
updateDocument(
uri: string,
changes: TextDocumentContentChangeEvent[]
) {
updateDocument(uri: string, changes: TextDocumentContentChangeEvent[]) {
const document = this.documents.get(normalizeUri(uri));
if (!document) {
throw new Error('Cannot call methods on an unopened document');
@ -89,9 +81,7 @@ export class DocumentManager {
}
getAllOpenedByClient() {
return Array.from(this.documents.entries()).filter((doc) =>
this.openedInClient.has(doc[0])
);
return Array.from(this.documents.entries()).filter((doc) => this.openedInClient.has(doc[0]));
}
on(name: DocumentEvent, listener: (document: Document) => void) {

View file

@ -11,17 +11,17 @@ interface Content {
}
export interface AstroDocument {
frontmatter: Frontmatter
frontmatter: Frontmatter;
content: Content;
}
/** Parses a document to collect metadata about Astro features */
export function parseAstro(content: string): AstroDocument {
const frontmatter = getFrontmatter(content)
const frontmatter = getFrontmatter(content);
return {
frontmatter,
content: getContent(content, frontmatter)
}
content: getContent(content, frontmatter),
};
}
/** Get frontmatter metadata */
@ -30,15 +30,18 @@ function getFrontmatter(content: string): Frontmatter {
function getFrontmatterState(): Frontmatter['state'] {
const parts = content.trim().split('---').length;
switch (parts) {
case 1: return null;
case 2: return 'open';
default: return 'closed';
case 1:
return null;
case 2:
return 'open';
default:
return 'closed';
}
}
const state = getFrontmatterState();
/** Construct a range containing the document's frontmatter */
function getFrontmatterOffsets(): [number|null, number|null] {
function getFrontmatterOffsets(): [number | null, number | null] {
const startOffset = content.indexOf('---');
if (startOffset === -1) return [null, null];
const endOffset = content.slice(startOffset + 3).indexOf('---') + 3;
@ -50,7 +53,7 @@ function getFrontmatter(content: string): Frontmatter {
return {
state,
startOffset,
endOffset
endOffset,
};
}
@ -59,16 +62,16 @@ function getContent(content: string, frontmatter: Frontmatter): Content {
switch (frontmatter.state) {
case null: {
const offset = getFirstNonWhitespaceIndex(content);
return { firstNonWhitespaceOffset: offset === -1 ? null : offset }
return { firstNonWhitespaceOffset: offset === -1 ? null : offset };
}
case 'open': {
return { firstNonWhitespaceOffset: null }
return { firstNonWhitespaceOffset: null };
}
case 'closed': {
const { endOffset } = frontmatter;
const end = (endOffset ?? 0) + 3;
const offset = getFirstNonWhitespaceIndex(content.slice(end))
return { firstNonWhitespaceOffset: end + offset }
const offset = getFirstNonWhitespaceIndex(content.slice(end));
return { firstNonWhitespaceOffset: end + offset };
}
}
}

View file

@ -1,12 +1,4 @@
import {
getLanguageService,
HTMLDocument,
TokenType,
ScannerState,
Scanner,
Node,
Position
} from 'vscode-html-languageservice';
import { getLanguageService, HTMLDocument, TokenType, ScannerState, Scanner, Node, Position } from 'vscode-html-languageservice';
import { Document } from './Document';
import { isInsideExpression } from './utils';
@ -24,11 +16,7 @@ export function parseHtml(text: string): HTMLDocument {
return parsedDoc;
}
const createScanner = parser.createScanner as (
input: string,
initialOffset?: number,
initialState?: ScannerState
) => Scanner;
const createScanner = parser.createScanner as (input: string, initialOffset?: number, initialState?: ScannerState) => Scanner;
/**
* scan the text and remove any `>` or `<` that cause the tag to end short,
@ -59,12 +47,7 @@ function preprocess(text: string) {
// <Foo checked={a < 1}>
// https://github.com/microsoft/vscode-html-languageservice/blob/71806ef57be07e1068ee40900ef8b0899c80e68a/src/parser/htmlScanner.ts#L327
if (
token === TokenType.Unknown &&
scanner.getScannerState() === ScannerState.WithinTag &&
scanner.getTokenText() === '<' &&
shouldBlankStartOrEndTagLike(offset)
) {
if (token === TokenType.Unknown && scanner.getScannerState() === ScannerState.WithinTag && scanner.getTokenText() === '<' && shouldBlankStartOrEndTagLike(offset)) {
blankStartOrEndTagLike(offset);
}
@ -75,10 +58,7 @@ function preprocess(text: string) {
function shouldBlankStartOrEndTagLike(offset: number) {
// not null rather than falsy, otherwise it won't work on first tag(0)
return (
currentStartTagStart !== null &&
isInsideExpression(text, currentStartTagStart, offset)
);
return currentStartTagStart !== null && isInsideExpression(text, currentStartTagStart, offset);
}
function blankStartOrEndTagLike(offset: number) {
@ -93,10 +73,7 @@ export interface AttributeContext {
valueRange?: [number, number];
}
export function getAttributeContextAtPosition(
document: Document,
position: Position
): AttributeContext | null {
export function getAttributeContextAtPosition(document: Document, position: Position): AttributeContext | null {
const offset = document.offsetAt(position);
const { html } = document;
const tag = html.findNodeAt(offset);
@ -106,15 +83,13 @@ export function getAttributeContextAtPosition(
}
const text = document.getText();
const beforeStartTagEnd =
text.substring(0, tag.start) + preprocess(text.substring(tag.start, tag.startTagEnd));
const beforeStartTagEnd = text.substring(0, tag.start) + preprocess(text.substring(tag.start, tag.startTagEnd));
const scanner = createScanner(beforeStartTagEnd, tag.start);
let token = scanner.scan();
let currentAttributeName: string | undefined;
const inTokenRange = () =>
scanner.getTokenOffset() <= offset && offset <= scanner.getTokenEnd();
const inTokenRange = () => scanner.getTokenOffset() <= offset && offset <= scanner.getTokenEnd();
while (token != TokenType.EOS) {
// adopted from https://github.com/microsoft/vscode-html-languageservice/blob/2f7ae4df298ac2c299a40e9024d118f4a9dc0c68/src/services/htmlCompletion.ts#L402
if (token === TokenType.AttributeName) {
@ -123,7 +98,7 @@ export function getAttributeContextAtPosition(
if (inTokenRange()) {
return {
name: currentAttributeName,
inValue: false
inValue: false,
};
}
} else if (token === TokenType.DelimiterAssign) {
@ -133,10 +108,7 @@ export function getAttributeContextAtPosition(
return {
name: currentAttributeName,
inValue: true,
valueRange: [
offset,
nextToken === TokenType.AttributeValue ? scanner.getTokenEnd() : offset
]
valueRange: [offset, nextToken === TokenType.AttributeValue ? scanner.getTokenEnd() : offset],
};
}
} else if (token === TokenType.AttributeValue) {
@ -153,7 +125,7 @@ export function getAttributeContextAtPosition(
return {
name: currentAttributeName,
inValue: true,
valueRange: [start, end]
valueRange: [start, end],
};
}
currentAttributeName = undefined;

View file

@ -5,11 +5,7 @@ import { clamp } from '../../utils';
* Gets word range at position.
* Delimiter is by default a whitespace, but can be adjusted.
*/
export function getWordRangeAt(
str: string,
pos: number,
delimiterRegex = { left: /\S+$/, right: /\s/ }
): { start: number; end: number } {
export function getWordRangeAt(str: string, pos: number, delimiterRegex = { left: /\S+$/, right: /\s/ }): { start: number; end: number } {
let start = str.slice(0, pos).search(delimiterRegex.left);
if (start < 0) {
start = pos;
@ -29,11 +25,7 @@ export function getWordRangeAt(
* Gets word at position.
* Delimiter is by default a whitespace, but can be adjusted.
*/
export function getWordAt(
str: string,
pos: number,
delimiterRegex = { left: /\S+$/, right: /\s/ }
): string {
export function getWordAt(str: string, pos: number, delimiterRegex = { left: /\S+$/, right: /\s/ }): string {
const { start, end } = getWordRangeAt(str, pos, delimiterRegex);
return str.slice(start, end);
}
@ -54,10 +46,7 @@ export function isInsideExpression(html: string, tagStart: number, position: num
/**
* Returns if a given offset is inside of the document frontmatter
*/
export function isInsideFrontmatter(
text: string,
offset: number
): boolean {
export function isInsideFrontmatter(text: string, offset: number): boolean {
let start = text.slice(0, offset).trim().split('---').length;
let end = text.slice(offset).trim().split('---').length;
@ -109,8 +98,7 @@ export function offsetAt(position: Position, text: string): number {
}
const lineOffset = lineOffsets[position.line];
const nextLineOffset =
position.line + 1 < lineOffsets.length ? lineOffsets[position.line + 1] : text.length;
const nextLineOffset = position.line + 1 < lineOffsets.length ? lineOffsets[position.line + 1] : text.length;
return clamp(nextLineOffset, lineOffset, lineOffset + position.character);
}

View file

@ -71,10 +71,12 @@ export function startServer() {
connection.onDidChangeTextDocument((evt) => docManager.updateDocument(evt.textDocument.uri, evt.contentChanges));
connection.onDidChangeWatchedFiles((evt) => {
const params = evt.changes.map(change => ({
const params = evt.changes
.map((change) => ({
fileName: urlToPath(change.uri),
changeType: change.type
})).filter(change => !!change.fileName)
changeType: change.type,
}))
.filter((change) => !!change.fileName);
pluginHost.onWatchFileChanges(params);
});

View file

@ -1,11 +1,4 @@
import {
CompletionContext,
CompletionItem,
CompletionList,
Position,
TextDocumentIdentifier,
} from 'vscode-languageserver';
import { CompletionContext, CompletionItem, CompletionList, Position, TextDocumentIdentifier } from 'vscode-languageserver';
import type { DocumentManager } from '../core/documents';
import type * as d from './interfaces';
import { flatten } from '../utils';
@ -15,7 +8,7 @@ import { FoldingRange } from 'vscode-languageserver-types';
enum ExecuteMode {
None,
FirstNonNull,
Collect
Collect,
}
export class PluginHost {
@ -27,81 +20,50 @@ export class PluginHost {
this.plugins.push(plugin);
}
async getCompletions(
textDocument: TextDocumentIdentifier,
position: Position,
completionContext?: CompletionContext
): Promise<CompletionList> {
async getCompletions(textDocument: TextDocumentIdentifier, position: Position, completionContext?: CompletionContext): Promise<CompletionList> {
const document = this.getDocument(textDocument.uri);
if (!document) {
throw new Error('Cannot call methods on an unopened document');
}
const completions = (
await this.execute<CompletionList>(
'getCompletions',
[document, position, completionContext],
ExecuteMode.Collect
)
).filter((completion) => completion != null);
const completions = (await this.execute<CompletionList>('getCompletions', [document, position, completionContext], ExecuteMode.Collect)).filter(
(completion) => completion != null
);
let flattenedCompletions = flatten(completions.map((completion) => completion.items));
const isIncomplete = completions.reduce(
(incomplete, completion) => incomplete || completion.isIncomplete,
false as boolean
);
const isIncomplete = completions.reduce((incomplete, completion) => incomplete || completion.isIncomplete, false as boolean);
return CompletionList.create(flattenedCompletions, isIncomplete);
}
async resolveCompletion(
textDocument: TextDocumentIdentifier,
completionItem: d.AppCompletionItem
): Promise<CompletionItem> {
async resolveCompletion(textDocument: TextDocumentIdentifier, completionItem: d.AppCompletionItem): Promise<CompletionItem> {
const document = this.getDocument(textDocument.uri);
if (!document) {
throw new Error('Cannot call methods on an unopened document');
}
const result = await this.execute<CompletionItem>(
'resolveCompletion',
[document, completionItem],
ExecuteMode.FirstNonNull
);
const result = await this.execute<CompletionItem>('resolveCompletion', [document, completionItem], ExecuteMode.FirstNonNull);
return result ?? completionItem;
}
async doTagComplete(
textDocument: TextDocumentIdentifier,
position: Position
): Promise<string | null> {
async doTagComplete(textDocument: TextDocumentIdentifier, position: Position): Promise<string | null> {
const document = this.getDocument(textDocument.uri);
if (!document) {
throw new Error('Cannot call methods on an unopened document');
}
return this.execute<string | null>(
'doTagComplete',
[document, position],
ExecuteMode.FirstNonNull
);
return this.execute<string | null>('doTagComplete', [document, position], ExecuteMode.FirstNonNull);
}
async getFoldingRanges(
textDocument: TextDocumentIdentifier
): Promise<FoldingRange[]|null> {
async getFoldingRanges(textDocument: TextDocumentIdentifier): Promise<FoldingRange[] | null> {
const document = this.getDocument(textDocument.uri);
if (!document) {
throw new Error('Cannot call methods on an unopened document');
}
const foldingRanges = flatten(await this.execute<FoldingRange[]>(
'getFoldingRanges',
[document],
ExecuteMode.Collect
)).filter((completion) => completion != null)
const foldingRanges = flatten(await this.execute<FoldingRange[]>('getFoldingRanges', [document], ExecuteMode.Collect)).filter((completion) => completion != null);
return foldingRanges;
}
@ -116,22 +78,10 @@ export class PluginHost {
return this.documentsManager.get(uri);
}
private execute<T>(
name: keyof d.LSProvider,
args: any[],
mode: ExecuteMode.FirstNonNull
): Promise<T | null>;
private execute<T>(
name: keyof d.LSProvider,
args: any[],
mode: ExecuteMode.Collect
): Promise<T[]>;
private execute<T>(name: keyof d.LSProvider, args: any[], mode: ExecuteMode.FirstNonNull): Promise<T | null>;
private execute<T>(name: keyof d.LSProvider, args: any[], mode: ExecuteMode.Collect): Promise<T[]>;
private execute(name: keyof d.LSProvider, args: any[], mode: ExecuteMode.None): Promise<void>;
private async execute<T>(
name: keyof d.LSProvider,
args: any[],
mode: ExecuteMode
): Promise<(T | null) | T[] | void> {
private async execute<T>(name: keyof d.LSProvider, args: any[], mode: ExecuteMode): Promise<(T | null) | T[] | void> {
const plugins = this.plugins.filter((plugin) => typeof plugin[name] === 'function');
switch (mode) {
@ -144,13 +94,9 @@ export class PluginHost {
}
return null;
case ExecuteMode.Collect:
return Promise.all(
plugins.map((plugin) => this.tryExecutePlugin(plugin, name, args, []))
);
return Promise.all(plugins.map((plugin) => this.tryExecutePlugin(plugin, name, args, [])));
case ExecuteMode.None:
await Promise.all(
plugins.map((plugin) => this.tryExecutePlugin(plugin, name, args, null))
);
await Promise.all(plugins.map((plugin) => this.tryExecutePlugin(plugin, name, args, null)));
return;
}
}

View file

@ -49,7 +49,7 @@ export class AstroPlugin implements CompletionsProvider, FoldingRangeProvider {
endLine: end.line,
endCharacter: end.character,
kind: FoldingRangeKind.Imports,
}
},
];
}

View file

@ -34,15 +34,7 @@ export class HTMLPlugin implements CompletionsProvider, FoldingRangeProvider {
isIncomplete: true,
items: [],
};
this.lang.setCompletionParticipants([
getEmmetCompletionParticipants(
document,
position,
'html',
this.configManager.getEmmetConfig(),
emmetResults
)
]);
this.lang.setCompletionParticipants([getEmmetCompletionParticipants(document, position, 'html', this.configManager.getEmmetConfig(), emmetResults)]);
const results = this.lang.doComplete(document, position, html);
const items = this.toCompletionItems(results.items);
@ -54,14 +46,13 @@ export class HTMLPlugin implements CompletionsProvider, FoldingRangeProvider {
);
}
getFoldingRanges(document: Document): FoldingRange[]|null {
getFoldingRanges(document: Document): FoldingRange[] | null {
const html = this.documents.get(document);
if (!html) {
return null;
}
return this.lang.getFoldingRanges(document);
}
doTagComplete(document: Document, position: Position): string | null {

View file

@ -1,11 +1,4 @@
import {
CompletionContext,
FileChangeType,
LinkedEditingRanges,
SemanticTokens,
SignatureHelpContext,
TextDocumentContentChangeEvent
} from 'vscode-languageserver';
import { CompletionContext, FileChangeType, LinkedEditingRanges, SemanticTokens, SignatureHelpContext, TextDocumentContentChangeEvent } from 'vscode-languageserver';
import {
CodeAction,
CodeActionContext,
@ -28,7 +21,7 @@ import {
WorkspaceEdit,
SelectionRange,
SignatureHelp,
FoldingRange
FoldingRange,
} from 'vscode-languageserver-types';
import { Document } from '../core/documents';
@ -51,20 +44,13 @@ export interface HoverProvider {
}
export interface FoldingRangeProvider {
getFoldingRanges(document: Document): Resolvable<FoldingRange[]|null>;
getFoldingRanges(document: Document): Resolvable<FoldingRange[] | null>;
}
export interface CompletionsProvider<T extends TextDocumentIdentifier = any> {
getCompletions(
document: Document,
position: Position,
completionContext?: CompletionContext
): Resolvable<AppCompletionList<T> | null>;
getCompletions(document: Document, position: Position, completionContext?: CompletionContext): Resolvable<AppCompletionList<T> | null>;
resolveCompletion?(
document: Document,
completionItem: AppCompletionItem<T>
): Resolvable<AppCompletionItem<T>>;
resolveCompletion?(document: Document, completionItem: AppCompletionItem<T>): Resolvable<AppCompletionItem<T>>;
}
export interface FormattingProvider {
@ -80,11 +66,7 @@ export interface DocumentColorsProvider {
}
export interface ColorPresentationsProvider {
getColorPresentations(
document: Document,
range: Range,
color: Color
): Resolvable<ColorPresentation[]>;
getColorPresentations(document: Document, range: Range, color: Color): Resolvable<ColorPresentation[]>;
}
export interface DocumentSymbolsProvider {
@ -96,23 +78,12 @@ export interface DefinitionsProvider {
}
export interface BackwardsCompatibleDefinitionsProvider {
getDefinitions(
document: Document,
position: Position
): Resolvable<DefinitionLink[] | Location[]>;
getDefinitions(document: Document, position: Position): Resolvable<DefinitionLink[] | Location[]>;
}
export interface CodeActionsProvider {
getCodeActions(
document: Document,
range: Range,
context: CodeActionContext
): Resolvable<CodeAction[]>;
executeCommand?(
document: Document,
command: string,
args?: any[]
): Resolvable<WorkspaceEdit | string | null>;
getCodeActions(document: Document, range: Range, context: CodeActionContext): Resolvable<CodeAction[]>;
executeCommand?(document: Document, command: string, args?: any[]): Resolvable<WorkspaceEdit | string | null>;
}
export interface FileRename {
@ -125,28 +96,16 @@ export interface UpdateImportsProvider {
}
export interface RenameProvider {
rename(
document: Document,
position: Position,
newName: string
): Resolvable<WorkspaceEdit | null>;
rename(document: Document, position: Position, newName: string): Resolvable<WorkspaceEdit | null>;
prepareRename(document: Document, position: Position): Resolvable<Range | null>;
}
export interface FindReferencesProvider {
findReferences(
document: Document,
position: Position,
context: ReferenceContext
): Promise<Location[] | null>;
findReferences(document: Document, position: Position, context: ReferenceContext): Promise<Location[] | null>;
}
export interface SignatureHelpProvider {
getSignatureHelp(
document: Document,
position: Position,
context: SignatureHelpContext | undefined
): Resolvable<SignatureHelp | null>;
getSignatureHelp(document: Document, position: Position, context: SignatureHelpContext | undefined): Resolvable<SignatureHelp | null>;
}
export interface SelectionRangeProvider {
@ -158,10 +117,7 @@ export interface SemanticTokensProvider {
}
export interface LinkedEditingRangesProvider {
getLinkedEditingRanges(
document: Document,
position: Position
): Resolvable<LinkedEditingRanges | null>;
getLinkedEditingRanges(document: Document, position: Position): Resolvable<LinkedEditingRanges | null>;
}
export interface OnWatchFileChangesPara {
@ -208,10 +164,4 @@ export interface LSPProviderConfig {
definitionLinkSupport: boolean;
}
export type Plugin = Partial<
ProviderBase &
DefinitionsProvider &
OnWatchFileChanges &
SelectionRangeProvider &
UpdateTsOrJsFile
>;
export type Plugin = Partial<ProviderBase & DefinitionsProvider & OnWatchFileChanges & SelectionRangeProvider & UpdateTsOrJsFile>;

View file

@ -38,7 +38,7 @@ export class LanguageServiceManager {
const url = urlToPath(curr) as string;
if (fileName.startsWith(url) && curr.length < url.length) return url;
return found;
}, '')
}, '');
}
private createDocument = (fileName: string, content: string) => {

View file

@ -14,34 +14,16 @@ export class SnapshotManager {
private documents: Map<string, DocumentSnapshot> = new Map();
private lastLogged = new Date(new Date().getTime() - 60_001);
private readonly watchExtensions = [
ts.Extension.Dts,
ts.Extension.Js,
ts.Extension.Jsx,
ts.Extension.Ts,
ts.Extension.Tsx,
ts.Extension.Json
];
private readonly watchExtensions = [ts.Extension.Dts, ts.Extension.Js, ts.Extension.Jsx, ts.Extension.Ts, ts.Extension.Tsx, ts.Extension.Json];
constructor(
private projectFiles: string[],
private fileSpec: TsFilesSpec,
private workspaceRoot: string
) {
}
constructor(private projectFiles: string[], private fileSpec: TsFilesSpec, private workspaceRoot: string) {}
updateProjectFiles() {
const { include, exclude } = this.fileSpec;
if (include?.length === 0) return;
const projectFiles = ts.sys.readDirectory(
this.workspaceRoot,
this.watchExtensions,
exclude,
include
);
const projectFiles = ts.sys.readDirectory(this.workspaceRoot, this.watchExtensions, exclude, include);
this.projectFiles = Array.from(new Set([...this.projectFiles, ...projectFiles]));
}
@ -88,7 +70,7 @@ export class SnapshotManager {
}
getFileNames() {
return Array.from(this.documents.keys()).map(fileName => toVirtualAstroFilePath(fileName));
return Array.from(this.documents.keys()).map((fileName) => toVirtualAstroFilePath(fileName));
}
getProjectFileNames() {
@ -106,12 +88,8 @@ export class SnapshotManager {
console.log(
'SnapshotManager File Statistics:\n' +
`Project files: ${projectFiles.length}\n` +
`Astro files: ${
allFiles.filter((name) => name.endsWith('.astro')).length
}\n` +
`From node_modules: ${
allFiles.filter((name) => name.includes('node_modules')).length
}\n` +
`Astro files: ${allFiles.filter((name) => name.endsWith('.astro')).length}\n` +
`From node_modules: ${allFiles.filter((name) => name.includes('node_modules')).length}\n` +
`Total: ${allFiles.length}`
);
}
@ -152,11 +130,9 @@ export const createDocumentSnapshot = (filePath: string, createDocument?: (_file
}
return new TypeScriptDocumentSnapshot(0, filePath, text);
}
};
class AstroDocumentSnapshot implements DocumentSnapshot {
version = this.doc.version;
scriptKind = ts.ScriptKind.Unknown;
@ -206,11 +182,9 @@ class AstroDocumentSnapshot implements DocumentSnapshot {
offsetAt(position: Position) {
return offsetAt(position, this.text);
}
}
class DocumentFragmentSnapshot implements Omit<DocumentSnapshot, 'getFragment'|'destroyFragment'> {
class DocumentFragmentSnapshot implements Omit<DocumentSnapshot, 'getFragment' | 'destroyFragment'> {
version: number;
filePath: string;
url: string;
@ -219,9 +193,7 @@ class DocumentFragmentSnapshot implements Omit<DocumentSnapshot, 'getFragment'|'
scriptKind = ts.ScriptKind.TSX;
scriptInfo = null;
constructor(
private doc: Document
) {
constructor(private doc: Document) {
const filePath = doc.getFilePath();
if (!filePath) throw new Error('Cannot create a document fragment from a non-local document');
const text = doc.getText();
@ -267,14 +239,12 @@ class DocumentFragmentSnapshot implements Omit<DocumentSnapshot, 'getFragment'|'
}
class TypeScriptDocumentSnapshot implements DocumentSnapshot {
scriptKind = getScriptKindFromFileName(this.filePath);
scriptInfo = null;
url: string;
constructor(public version: number, public readonly filePath: string, private text: string) {
this.url = pathToUrl(filePath)
this.url = pathToUrl(filePath);
}
getText(start: number, end: number) {
@ -302,7 +272,7 @@ class TypeScriptDocumentSnapshot implements DocumentSnapshot {
}
async getFragment(): Promise<DocumentFragmentSnapshot> {
return this as unknown as any;
return (this as unknown) as any;
}
destroyFragment() {

View file

@ -1,11 +1,7 @@
import type { Document, DocumentManager } from '../../core/documents';
import type { ConfigManager } from '../../core/config';
import type { CompletionsProvider, AppCompletionItem, AppCompletionList } from '../interfaces';
import {
CompletionContext,
Position,
FileChangeType
} from 'vscode-languageserver';
import { CompletionContext, Position, FileChangeType } from 'vscode-languageserver';
import * as ts from 'typescript';
import { CompletionsProviderImpl, CompletionEntryWithIdentifer } from './features/CompletionsProvider';
import { LanguageServiceManager } from './LanguageServiceManager';
@ -19,11 +15,7 @@ export class TypeScriptPlugin implements CompletionsProvider {
private readonly completionProvider: CompletionsProviderImpl;
constructor(
docManager: DocumentManager,
configManager: ConfigManager,
workspaceUris: string[]
) {
constructor(docManager: DocumentManager, configManager: ConfigManager, workspaceUris: string[]) {
this.docManager = docManager;
this.configManager = configManager;
this.languageServiceManager = new LanguageServiceManager(docManager, configManager, workspaceUris);
@ -31,24 +23,13 @@ export class TypeScriptPlugin implements CompletionsProvider {
this.completionProvider = new CompletionsProviderImpl(this.languageServiceManager);
}
async getCompletions(
document: Document,
position: Position,
completionContext?: CompletionContext
): Promise<AppCompletionList<CompletionEntryWithIdentifer> | null> {
const completions = await this.completionProvider.getCompletions(
document,
position,
completionContext
);
async getCompletions(document: Document, position: Position, completionContext?: CompletionContext): Promise<AppCompletionList<CompletionEntryWithIdentifer> | null> {
const completions = await this.completionProvider.getCompletions(document, position, completionContext);
return completions;
}
async resolveCompletion(
document: Document,
completionItem: AppCompletionItem<CompletionEntryWithIdentifer>
): Promise<AppCompletionItem<CompletionEntryWithIdentifer>> {
async resolveCompletion(document: Document, completionItem: AppCompletionItem<CompletionEntryWithIdentifer>): Promise<AppCompletionItem<CompletionEntryWithIdentifer>> {
return this.completionProvider.resolveCompletion(document, completionItem);
}
@ -86,4 +67,3 @@ export class TypeScriptPlugin implements CompletionsProvider {
return this.languageServiceManager.getSnapshotManager(fileName);
}
}

View file

@ -23,9 +23,9 @@ export function createAstroSys(getSnapshot: (fileName: string) => DocumentSnapsh
},
readDirectory(path, extensions, exclude, include, depth) {
const extensionsWithAstro = (extensions ?? []).concat(...['.astro']);
const result = ts.sys.readDirectory(path, extensionsWithAstro, exclude, include, depth);;
const result = ts.sys.readDirectory(path, extensionsWithAstro, exclude, include, depth);
return result;
}
},
};
if (ts.sys.realpath) {

View file

@ -99,7 +99,7 @@ export class CompletionsProviderImpl implements CompletionsProvider<CompletionEn
data: {
...comp,
uri,
position
position,
},
};
}

View file

@ -58,25 +58,17 @@ async function createLanguageService(tsconfigPath: string, workspaceRoot: string
if (!configJson.extends) {
configJson = Object.assign(
{
exclude: getDefaultExclude()
exclude: getDefaultExclude(),
},
configJson
);
}
const project = ts.parseJsonConfigFileContent(
configJson,
parseConfigHost,
workspaceRoot,
{},
basename(tsconfigPath),
undefined,
[
const project = ts.parseJsonConfigFileContent(configJson, parseConfigHost, workspaceRoot, {}, basename(tsconfigPath), undefined, [
{ extension: '.vue', isMixedContent: true, scriptKind: ts.ScriptKind.Deferred },
{ extension: '.svelte', isMixedContent: true, scriptKind: ts.ScriptKind.Deferred },
{ extension: '.astro', isMixedContent: true, scriptKind: ts.ScriptKind.Deferred }
]
);
{ extension: '.astro', isMixedContent: true, scriptKind: ts.ScriptKind.Deferred },
]);
let projectVersion = 0;
const snapshotManager = new SnapshotManager(project.fileNames, { exclude: ['node_modules', 'dist'], include: ['astro'] }, workspaceRoot || process.cwd());
@ -100,15 +92,15 @@ async function createLanguageService(tsconfigPath: string, workspaceRoot: string
getProjectVersion: () => `${projectVersion}`,
getScriptFileNames: () => Array.from(new Set([...snapshotManager.getFileNames(), ...snapshotManager.getProjectFileNames()])),
getScriptSnapshot,
getScriptVersion: (fileName: string) => getScriptSnapshot(fileName).version.toString()
getScriptVersion: (fileName: string) => getScriptSnapshot(fileName).version.toString(),
};
const languageService = ts.createLanguageService(host);
const languageServiceProxy = new Proxy(languageService, {
get(target, prop) {
return Reflect.get(target, prop);
}
})
},
});
return {
tsconfigPath,
@ -148,10 +140,7 @@ async function createLanguageService(tsconfigPath: string, workspaceRoot: string
return doc;
}
doc = createDocumentSnapshot(
fileName,
docContext.createDocument,
);
doc = createDocumentSnapshot(fileName, docContext.createDocument);
snapshotManager.set(fileName, doc);
return doc;
}
@ -168,7 +157,7 @@ function getDefaultJsConfig(): {
compilerOptions: {
maxNodeModuleJsDepth: 2,
allowSyntheticDefaultImports: true,
allowJs: true
allowJs: true,
},
include: ['astro'],
};

View file

@ -3,9 +3,7 @@ import { CompletionItemKind, DiagnosticSeverity } from 'vscode-languageserver';
import { dirname } from 'path';
import { pathToUrl } from '../../utils';
export function scriptElementKindToCompletionItemKind(
kind: ts.ScriptElementKind
): CompletionItemKind {
export function scriptElementKindToCompletionItemKind(kind: ts.ScriptElementKind): CompletionItemKind {
switch (kind) {
case ts.ScriptElementKind.primitiveType:
case ts.ScriptElementKind.keyword:
@ -49,9 +47,7 @@ export function scriptElementKindToCompletionItemKind(
return CompletionItemKind.Property;
}
export function getCommitCharactersForScriptElement(
kind: ts.ScriptElementKind
): string[] | undefined {
export function getCommitCharactersForScriptElement(kind: ts.ScriptElementKind): string[] | undefined {
const commitCharacters: string[] = [];
switch (kind) {
case ts.ScriptElementKind.memberGetAccessorElement:
@ -137,10 +133,7 @@ export function ensureRealAstroFilePath(filePath: string) {
export function findTsConfigPath(fileName: string, rootUris: string[]) {
const searchDir = dirname(fileName);
const path =
ts.findConfigFile(searchDir, ts.sys.fileExists, 'tsconfig.json') ||
ts.findConfigFile(searchDir, ts.sys.fileExists, 'jsconfig.json') ||
'';
const path = ts.findConfigFile(searchDir, ts.sys.fileExists, 'tsconfig.json') || ts.findConfigFile(searchDir, ts.sys.fileExists, 'jsconfig.json') || '';
// Don't return config files that exceed the current workspace context.
return !!path && rootUris.some((rootUri) => isSubPath(rootUri, path)) ? path : '';
}
@ -150,7 +143,6 @@ export function isSubPath(uri: string, possibleSubPath: string): boolean {
return pathToUrl(possibleSubPath).startsWith(uri);
}
/** Substitutes */
export function substituteWithWhitespace(result: string, start: number, end: number, oldContent: string, before: string, after: string) {
let accumulatedWS = 0;

View file

@ -21,23 +21,22 @@ export function pathToUrl(path: string) {
return URI.file(path).toString();
}
/**
*
* The language service is case insensitive, and would provide
* hover info for Svelte components like `Option` which have
* the same name like a html tag.
*/
*
* The language service is case insensitive, and would provide
* hover info for Svelte components like `Option` which have
* the same name like a html tag.
*/
export function isPossibleComponent(node: Node): boolean {
return !!node.tag?.[0].match(/[A-Z]/);
}
/**
*
* The language service is case insensitive, and would provide
* hover info for Svelte components like `Option` which have
* the same name like a html tag.
*/
*
* The language service is case insensitive, and would provide
* hover info for Svelte components like `Option` which have
* the same name like a html tag.
*/
export function isPossibleClientComponent(node: Node): boolean {
return isPossibleComponent(node) && (node.tag?.indexOf(':') ?? -1) > -1;
}
@ -54,18 +53,12 @@ export function clamp(num: number, min: number, max: number): number {
/** Checks if a position is inside range */
export function isInRange(positionToTest: Position, range: Range): boolean {
return (
isBeforeOrEqualToPosition(range.end, positionToTest) &&
isBeforeOrEqualToPosition(positionToTest, range.start)
);
return isBeforeOrEqualToPosition(range.end, positionToTest) && isBeforeOrEqualToPosition(positionToTest, range.start);
}
/** */
export function isBeforeOrEqualToPosition(position: Position, positionToTest: Position): boolean {
return (
positionToTest.line < position.line ||
(positionToTest.line === position.line && positionToTest.character <= position.character)
);
return positionToTest.line < position.line || (positionToTest.line === position.line && positionToTest.character <= position.character);
}
/**
@ -76,11 +69,7 @@ export function isBeforeOrEqualToPosition(position: Position, positionToTest: Po
* @param determineIfSame The function which determines if the previous invocation should be canceld or not
* @param miliseconds Number of miliseconds to debounce
*/
export function debounceSameArg<T>(
fn: (arg: T) => void,
shouldCancelPrevious: (newArg: T, prevArg?: T) => boolean,
miliseconds: number
): (arg: T) => void {
export function debounceSameArg<T>(fn: (arg: T) => void, shouldCancelPrevious: (newArg: T, prevArg?: T) => boolean, miliseconds: number): (arg: T) => void {
let timeout: any;
let prevArg: T | undefined;

View file

@ -2,8 +2,8 @@
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src",
"rootDir": "src"
},
"include": ["src"],
"exclude": ["node_modules"],
"exclude": ["node_modules"]
}

View file

@ -1,9 +1,7 @@
{
"fileTypes": [
"astro"
],
"foldingStartMarker": "(?x)\n(<(?i:head|body|table|thead|tbody|tfoot|tr|div|select|fieldset|style|script|ul|ol|li|form|dl)\\b.*?>\n|<!--(?!.*--\\s*>)\n|^<!--\\ \\#tminclude\\ (?>.*?-->)$\n|<\\?(?:php)?.*\\b(if|for(each)?|while)\\b.+:\n|\\{\\{?(if|foreach|capture|literal|foreach|php|section|strip)\n|\\{\\s*($|\\?>\\s*$|\/\/|\/\\*(.*\\*\/\\s*$|(?!.*?\\*\/)))\n)",
"foldingStopMarker": "(?x)\n(<\/(?i:head|body|table|thead|tbody|tfoot|tr|div|select|fieldset|style|script|ul|ol|li|form|dl)>\n|^(?!.*?<!--).*?--\\s*>\n|^<!--\\ end\\ tminclude\\ -->$\n|<\\?(?:php)?.*\\bend(if|for(each)?|while)\\b\n|\\{\\{?\/(if|foreach|capture|literal|foreach|php|section|strip)\n|^[^{]*\\}\n)",
"fileTypes": ["astro"],
"foldingStartMarker": "(?x)\n(<(?i:head|body|table|thead|tbody|tfoot|tr|div|select|fieldset|style|script|ul|ol|li|form|dl)\\b.*?>\n|<!--(?!.*--\\s*>)\n|^<!--\\ \\#tminclude\\ (?>.*?-->)$\n|<\\?(?:php)?.*\\b(if|for(each)?|while)\\b.+:\n|\\{\\{?(if|foreach|capture|literal|foreach|php|section|strip)\n|\\{\\s*($|\\?>\\s*$|//|/\\*(.*\\*/\\s*$|(?!.*?\\*/)))\n)",
"foldingStopMarker": "(?x)\n(</(?i:head|body|table|thead|tbody|tfoot|tr|div|select|fieldset|style|script|ul|ol|li|form|dl)>\n|^(?!.*?<!--).*?--\\s*>\n|^<!--\\ end\\ tminclude\\ -->$\n|<\\?(?:php)?.*\\bend(if|for(each)?|while)\\b\n|\\{\\{?/(if|foreach|capture|literal|foreach|php|section|strip)\n|^[^{]*\\}\n)",
"keyEquivalent": "^~H",
"name": "Astro",
"patterns": [
@ -11,7 +9,7 @@
"include": "#astro-expressions"
},
{
"begin": "(<)([a-zA-Z0-9:-]++)(?=[^>]*><\/\\2>)",
"begin": "(<)([a-zA-Z0-9:-]++)(?=[^>]*></\\2>)",
"beginCaptures": {
"1": {
"name": "punctuation.definition.tag.begin.html"
@ -20,7 +18,7 @@
"name": "entity.name.tag.html"
}
},
"end": "(>)(<)(\/)(\\2)(>)",
"end": "(>)(<)(/)(\\2)(>)",
"endCaptures": {
"1": {
"name": "punctuation.definition.tag.end.html"
@ -135,7 +133,7 @@
"name": "punctuation.definition.tag.html"
}
},
"end": "(<\/)((?i:style))(>)(?:\\s*\\n)?",
"end": "(</)((?i:style))(>)(?:\\s*\\n)?",
"name": "source.css.embedded.html",
"patterns": [
{
@ -148,7 +146,7 @@
"name": "punctuation.definition.tag.end.html"
}
},
"end": "(?=<\/(?i:style))",
"end": "(?=</(?i:style))",
"patterns": [
{
"include": "source.css"
@ -170,7 +168,7 @@
"name": "punctuation.definition.tag.html"
}
},
"end": "(<\/)((?i:style))(>)(?:\\s*\\n)?",
"end": "(</)((?i:style))(>)(?:\\s*\\n)?",
"name": "source.sass.embedded.html",
"patterns": [
{
@ -183,7 +181,7 @@
"name": "punctuation.definition.tag.end.html"
}
},
"end": "(?=<\/(?i:style))",
"end": "(?=</(?i:style))",
"patterns": [
{
"include": "source.sass"
@ -205,7 +203,7 @@
"name": "punctuation.definition.tag.html"
}
},
"end": "(<\/)((?i:style))(>)(?:\\s*\\n)?",
"end": "(</)((?i:style))(>)(?:\\s*\\n)?",
"name": "source.scss.embedded.html",
"patterns": [
{
@ -218,7 +216,7 @@
"name": "punctuation.definition.tag.end.html"
}
},
"end": "(?=<\/(?i:style))",
"end": "(?=</(?i:style))",
"patterns": [
{
"include": "source.css.scss"
@ -228,7 +226,7 @@
]
},
{
"begin": "(?:^\\s+)?(<)((?i:style))\\b(?![^>]*\/>)",
"begin": "(?:^\\s+)?(<)((?i:style))\\b(?![^>]*/>)",
"captures": {
"1": {
"name": "punctuation.definition.tag.begin.html"
@ -240,8 +238,10 @@
"name": "punctuation.definition.tag.html"
}
},
"end": "(<\/)((?i:style))(>)(?:\\s*\\n)?",
"__DEFAULT_STYLE_NAME_START__": null,"name": "source.css.embedded.html","__DEFAULT_STYLE_NAME_END__": null,
"end": "(</)((?i:style))(>)(?:\\s*\\n)?",
"__DEFAULT_STYLE_NAME_START__": null,
"name": "source.css.embedded.html",
"__DEFAULT_STYLE_NAME_END__": null,
"patterns": [
{
"include": "#tag-stuff"
@ -253,10 +253,12 @@
"name": "punctuation.definition.tag.end.html"
}
},
"end": "(?=<\/(?i:style))",
"end": "(?=</(?i:style))",
"patterns": [
{
"__DEFAULT_STYLE_INCLUDE_START__": null,"include": "source.css","__DEFAULT_STYLE_INCLUDE_END__": null
"__DEFAULT_STYLE_INCLUDE_START__": null,
"include": "source.css",
"__DEFAULT_STYLE_INCLUDE_END__": null
}
]
}
@ -272,7 +274,7 @@
"name": "entity.name.tag.script.html"
}
},
"end": "(?<=<\/(script|SCRIPT))(>)(?:\\s*\\n)?",
"end": "(?<=</(script|SCRIPT))(>)(?:\\s*\\n)?",
"endCaptures": {
"2": {
"name": "punctuation.definition.tag.html"
@ -284,7 +286,7 @@
"include": "#tag-stuff"
},
{
"begin": "(?<!<\/(?:script|SCRIPT))(>)",
"begin": "(?<!</(?:script|SCRIPT))(>)",
"captures": {
"1": {
"name": "punctuation.definition.tag.begin.html"
@ -293,7 +295,7 @@
"name": "entity.name.tag.script.html"
}
},
"end": "(<\/)((?i:script))",
"end": "(</)((?i:script))",
"patterns": [
{
"include": "source.tsx"
@ -312,7 +314,7 @@
"name": "entity.name.tag.script.html"
}
},
"end": "(?<=<\/(script|SCRIPT))(>)(?:\\s*\\n)?",
"end": "(?<=</(script|SCRIPT))(>)(?:\\s*\\n)?",
"endCaptures": {
"2": {
"name": "punctuation.definition.tag.html"
@ -324,7 +326,7 @@
"include": "#tag-stuff"
},
{
"begin": "(?<!<\/(?:script|SCRIPT))(>)",
"begin": "(?<!</(?:script|SCRIPT))(>)",
"captures": {
"1": {
"name": "punctuation.definition.tag.begin.html"
@ -333,7 +335,7 @@
"name": "entity.name.tag.script.html"
}
},
"end": "(<\/)((?i:script))",
"end": "(</)((?i:script))",
"patterns": [
{
"include": "source.tsx"
@ -343,7 +345,7 @@
]
},
{
"begin": "(<)((?i:script))\\b(?![^>]*\/>)(?![^>]*(?i:type.?=.?text\/((?!javascript|babel|ecmascript).*)))",
"begin": "(<)((?i:script))\\b(?![^>]*/>)(?![^>]*(?i:type.?=.?text/((?!javascript|babel|ecmascript).*)))",
"beginCaptures": {
"1": {
"name": "punctuation.definition.tag.begin.html"
@ -352,7 +354,7 @@
"name": "entity.name.tag.script.html"
}
},
"end": "(?<=<\/(script|SCRIPT))(>)(?:\\s*\\n)?",
"end": "(?<=</(script|SCRIPT))(>)(?:\\s*\\n)?",
"endCaptures": {
"2": {
"name": "punctuation.definition.tag.html"
@ -364,7 +366,7 @@
"include": "#tag-stuff"
},
{
"begin": "(?<!<\/(?:script|SCRIPT))(>)",
"begin": "(?<!</(?:script|SCRIPT))(>)",
"captures": {
"1": {
"name": "punctuation.definition.tag.begin.html"
@ -373,7 +375,7 @@
"name": "entity.name.tag.script.html"
}
},
"end": "(<\/)((?i:script))",
"end": "(</)((?i:script))",
"patterns": [
{
"captures": {
@ -381,17 +383,17 @@
"name": "punctuation.definition.comment.js"
}
},
"match": "(\/\/).*?((?=<\/script)|$\\n?)",
"match": "(//).*?((?=</script)|$\\n?)",
"name": "comment.line.double-slash.js"
},
{
"begin": "\/\\*",
"begin": "/\\*",
"captures": [
{
"name": "punctuation.definition.comment.js"
}
],
"end": "\\*\/|(?=<\/script)",
"end": "\\*/|(?=</script)",
"name": "comment.block.js"
},
{
@ -402,7 +404,7 @@
]
},
{
"begin": "(<\/?)((?i:body|head|html)\\b)",
"begin": "(</?)((?i:body|head|html)\\b)",
"captures": {
"1": {
"name": "punctuation.definition.tag.begin.html"
@ -425,7 +427,7 @@
]
},
{
"begin": "(<\/?)((?i:address|blockquote|dd|div|dl|dt|fieldset|form|frame|frameset|h1|h2|h3|h4|h5|h6|iframe|noframes|object|ol|p|ul|applet|center|dir|hr|menu|pre)\\b)",
"begin": "(</?)((?i:address|blockquote|dd|div|dl|dt|fieldset|form|frame|frameset|h1|h2|h3|h4|h5|h6|iframe|noframes|object|ol|p|ul|applet|center|dir|hr|menu|pre)\\b)",
"beginCaptures": {
"1": {
"name": "punctuation.definition.tag.begin.html"
@ -448,7 +450,7 @@
]
},
{
"begin": "(<\/?)((?i:a|abbr|acronym|area|b|base|basefont|bdo|big|br|button|caption|cite|code|col|colgroup|del|dfn|em|font|head|html|i|img|input|ins|isindex|kbd|label|legend|li|link|map|meta|noscript|optgroup|option|param|q|s|samp|script|select|small|span|strike|strong|style|sub|sup|table|tbody|td|textarea|tfoot|th|thead|title|tr|tt|u|var)\\b)",
"begin": "(</?)((?i:a|abbr|acronym|area|b|base|basefont|bdo|big|br|button|caption|cite|code|col|colgroup|del|dfn|em|font|head|html|i|img|input|ins|isindex|kbd|label|legend|li|link|map|meta|noscript|optgroup|option|param|q|s|samp|script|select|small|span|strike|strong|style|sub|sup|table|tbody|td|textarea|tfoot|th|thead|title|tr|tt|u|var)\\b)",
"beginCaptures": {
"1": {
"name": "punctuation.definition.tag.begin.html"
@ -457,7 +459,7 @@
"name": "entity.name.tag.inline.any.html"
}
},
"end": "((?: ?\/)?>)",
"end": "((?: ?/)?>)",
"endCaptures": {
"1": {
"name": "punctuation.definition.tag.end.html"
@ -471,7 +473,7 @@
]
},
{
"begin": "(<\/?)([a-zA-Z0-9:-]+)",
"begin": "(</?)([a-zA-Z0-9:-]+)",
"beginCaptures": {
"1": {
"name": "punctuation.definition.tag.begin.html"

View file

@ -1,4 +1,3 @@
{
"compilerOptions": {
"target": "es2019",
@ -13,6 +12,6 @@
"baseUrl": "./",
"paths": {
"@astro-vscode/*": ["packages/*/src"]
},
},
}
}
}

View file

@ -1,4 +1,3 @@
{
"extends": "./tsconfig.base.json",
"files": [],

View file

@ -1,4 +1,3 @@
module.exports = {
workspaceRoot: '../'
workspaceRoot: '../',
};

View file

@ -1656,7 +1656,7 @@
resolved "https://registry.npmjs.org/@types/yoga-layout/-/yoga-layout-1.9.2.tgz"
integrity sha512-S9q47ByT2pPvD65IvrWp7qppVMpk9WGMbVq9wbWZOHg6tnXSD4vyhao6nOSBwwfDdV2p3Kx9evA9vI+XWTfDvw==
"@typescript-eslint/eslint-plugin@^4.18.0":
"@typescript-eslint/eslint-plugin@^4.22.0":
version "4.22.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.22.0.tgz#3d5f29bb59e61a9dba1513d491b059e536e16dbc"
integrity sha512-U8SP9VOs275iDXaL08Ln1Fa/wLXfj5aTr/1c0t0j6CdbOnxh+TruXu1p4I0NAvdPBQgoPjHsgKn28mOi0FzfoA==
@ -4278,12 +4278,12 @@ escape-string-regexp@^2.0.0:
resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz"
integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==
eslint-config-prettier@^8.1.0:
eslint-config-prettier@^8.3.0:
version "8.3.0"
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz#f7471b20b6fe8a9a9254cc684454202886a2dd7a"
integrity sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==
eslint-plugin-prettier@^3.3.1:
eslint-plugin-prettier@^3.4.0:
version "3.4.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.0.tgz#cdbad3bf1dbd2b177e9825737fe63b476a08f0c7"
integrity sha512-UDK6rJT6INSfcOo545jiaOwB701uAIt2/dR7WnFQoGCVl1/EMqdANBmwUaqqQ45aXprsTGzSa39LI1PyuRBxxw==
@ -4315,7 +4315,7 @@ eslint-visitor-keys@^2.0.0:
resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz"
integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==
eslint@^7.22.0:
eslint@^7.25.0:
version "7.25.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.25.0.tgz#1309e4404d94e676e3e831b3a3ad2b050031eb67"
integrity sha512-TVpSovpvCNpLURIScDRB6g5CYu/ZFq9GfX2hLNIV4dSBKxIWojeDODvYl3t0k0VtMxYeR8OXPCFE5+oHMlGfhw==