Skip to content

Commit f445802

Browse files
Rich-HarrisTheo-Steinerbenmccann
authored
Add JSDoc annotations to templates (#4621)
* Set checkJs to false in no-typescript scaffold * [enhance] add selection for type checking to create-svelte * Add changelog * Adjust file inclusion logic to checkjs option * add jsdoc annotations * strip jsdoc from source * tidy up * oops * Update packages/create-svelte/index.js Co-authored-by: Ben McCann <[email protected]> * strip jsdoc annotations when no typechecking is selected Co-authored-by: Theo Steiner <[email protected]> Co-authored-by: Ben McCann <[email protected]>
1 parent 4a2590f commit f445802

File tree

17 files changed

+228
-99
lines changed

17 files changed

+228
-99
lines changed

.changeset/cold-boats-wave.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'create-svelte': patch
3+
---
4+
5+
Disable type checking by default for non-typescript projects.

packages/create-svelte/bin.js

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,15 @@ async function main() {
7070
})
7171
},
7272
{
73-
type: 'toggle',
74-
name: 'typescript',
75-
message: 'Use TypeScript?',
73+
type: 'select',
74+
name: 'types',
75+
message: 'Add type checking?',
7676
initial: false,
77-
active: 'Yes',
78-
inactive: 'No'
77+
choices: [
78+
{ title: 'Type-checked JavaScript', value: 'checkjs' },
79+
{ title: 'TypeScript', value: 'typescript' },
80+
{ title: 'None', value: null }
81+
]
7982
},
8083
{
8184
type: 'toggle',
@@ -116,9 +119,12 @@ async function main() {
116119

117120
console.log(bold(green('\nYour project is ready!')));
118121

119-
if (options.typescript) {
122+
if (options.types === 'typescript') {
120123
console.log(bold('✔ Typescript'));
121124
console.log(' Inside Svelte components, use <script lang="ts">');
125+
} else if (options.types === 'checkjs') {
126+
console.log(bold('✔ Type-checked JavaScript'));
127+
console.log(' https://www.typescriptlang.org/tsconfig#checkJs');
122128
}
123129

124130
if (options.eslint) {

packages/create-svelte/index.js

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,22 @@ import { mkdirp, copy, dist } from './utils.js';
1111
export async function create(cwd, options) {
1212
mkdirp(cwd);
1313

14-
write_template_files(options.template, options.typescript, options.name, cwd);
14+
write_template_files(options.template, options.types, options.name, cwd);
1515
write_common_files(cwd, options, options.name);
1616
}
1717

1818
/**
1919
* @param {string} template
20-
* @param {boolean} typescript
20+
* @param {'typescript' | 'checkjs' | null} types
2121
* @param {string} name
2222
* @param {string} cwd
2323
*/
24-
function write_template_files(template, typescript, name, cwd) {
24+
function write_template_files(template, types, name, cwd) {
2525
const dir = dist(`templates/${template}`);
2626
copy(`${dir}/assets`, cwd, (name) => name.replace('DOT-', '.'));
2727
copy(`${dir}/package.json`, `${cwd}/package.json`);
2828

29-
const manifest = `${dir}/files.${typescript ? 'ts' : 'js'}.json`;
29+
const manifest = `${dir}/files.types=${types}.json`;
3030
const files = /** @type {import('./types/internal').File[]} */ (
3131
JSON.parse(fs.readFileSync(manifest, 'utf-8'))
3232
);
@@ -83,9 +83,13 @@ function write_common_files(cwd, options, name) {
8383
* @returns {boolean}
8484
*/
8585
function matches_condition(condition, options) {
86-
return condition === 'default' || condition === 'skeleton'
87-
? options.template === condition
88-
: options[condition];
86+
if (condition === 'default' || condition === 'skeleton') {
87+
return options.template === condition;
88+
}
89+
if (condition === 'typescript' || condition === 'checkjs') {
90+
return options.types === condition;
91+
}
92+
return options[condition];
8993
}
9094

9195
/**

packages/create-svelte/scripts/build-templates.js

Lines changed: 101 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import { transform } from 'sucrase';
66
import glob from 'tiny-glob/sync.js';
77
import { mkdirp, rimraf } from '../utils.js';
88

9-
/** @param {string} typescript */
10-
function convert_typescript(typescript) {
11-
const transformed = transform(typescript, {
9+
/** @param {string} content */
10+
function convert_typescript(content) {
11+
const transformed = transform(content, {
1212
transforms: ['typescript']
1313
});
1414

@@ -21,6 +21,11 @@ function convert_typescript(typescript) {
2121
});
2222
}
2323

24+
/** @param {string} content */
25+
function strip_jsdoc(content) {
26+
return content.replace(/\/\*\*[\s\S]+?\*\/[\s\n]+/g, '');
27+
}
28+
2429
/** @param {Set<string>} shared */
2530
async function generate_templates(shared) {
2631
const templates = fs.readdirSync('templates');
@@ -43,8 +48,12 @@ async function generate_templates(shared) {
4348
const meta_file = path.join(cwd, '.meta.json');
4449
if (!fs.existsSync(meta_file)) throw new Error('Template must have a .meta.json file');
4550

46-
/** @type {import('../types/internal.js').File[]} */
47-
const ts = [];
51+
/** @type {Record<string, import('../types/internal.js').File[]>} */
52+
const types = {
53+
typescript: [],
54+
checkjs: [],
55+
null: []
56+
};
4857

4958
glob('**/*', { cwd, filesOnly: true, dot: true }).forEach((name) => {
5059
// the package.template.json thing is a bit annoying — basically we want
@@ -64,88 +73,102 @@ async function generate_templates(shared) {
6473
// ignore contents of .gitignore or .ignore
6574
if (!gitignore.accepts(name) || !ignore.accepts(name) || name === '.ignore') return;
6675

67-
if (/\.(js|ts|svelte|svelte\.md)$/.test(name)) {
76+
if (/\.(ts|svelte)$/.test(name)) {
6877
const contents = fs.readFileSync(path.join(cwd, name), 'utf8');
69-
ts.push({
70-
name,
71-
contents
72-
});
78+
79+
if (name.endsWith('.d.ts')) {
80+
if (name.endsWith('app.d.ts')) types.checkjs.push({ name, contents });
81+
types.typescript.push({ name, contents });
82+
} else if (name.endsWith('.ts')) {
83+
const js = convert_typescript(contents);
84+
85+
types.typescript.push({
86+
name,
87+
contents: strip_jsdoc(contents)
88+
});
89+
90+
types.checkjs.push({
91+
name: name.replace(/\.ts$/, '.js'),
92+
contents: js
93+
});
94+
95+
types.null.push({
96+
name: name.replace(/\.ts$/, '.js'),
97+
contents: strip_jsdoc(js)
98+
});
99+
} else {
100+
// we jump through some hoops, rather than just using svelte.preprocess,
101+
// so that the output preserves the original formatting to the extent
102+
// possible (e.g. preserving double line breaks). Sucrase is the best
103+
// tool for the job because it just removes the types; Prettier then
104+
// tidies up the end result
105+
const js_contents = contents.replace(
106+
/<script([^>]+)>([\s\S]+?)<\/script>/g,
107+
(m, attrs, typescript) => {
108+
// Sucrase assumes 'unused' imports (which _are_ used, but only
109+
// in the markup) are type imports, and strips them. This step
110+
// prevents it from drawing that conclusion
111+
const imports = [];
112+
const import_pattern = /import (.+?) from/g;
113+
let import_match;
114+
while ((import_match = import_pattern.exec(typescript))) {
115+
const word_pattern = /[a-z_$][a-z0-9_$]*/gi;
116+
let word_match;
117+
while ((word_match = word_pattern.exec(import_match[1]))) {
118+
imports.push(word_match[0]);
119+
}
120+
}
121+
122+
const suffix = `\n${imports.join(',')}`;
123+
124+
const transformed = transform(typescript + suffix, {
125+
transforms: ['typescript']
126+
}).code.slice(0, -suffix.length);
127+
128+
const contents = prettier
129+
.format(transformed, {
130+
parser: 'babel',
131+
useTabs: true,
132+
singleQuote: true,
133+
trailingComma: 'none',
134+
printWidth: 100
135+
})
136+
.trim()
137+
.replace(/^(.)/gm, '\t$1');
138+
139+
return `<script${attrs.replace(' lang="ts"', '')}>\n${contents}\n</script>`;
140+
}
141+
);
142+
143+
types.typescript.push({
144+
name,
145+
contents: strip_jsdoc(contents)
146+
});
147+
148+
types.checkjs.push({
149+
name,
150+
contents: js_contents
151+
});
152+
153+
types.null.push({
154+
name,
155+
contents: strip_jsdoc(js_contents)
156+
});
157+
}
73158
} else {
74159
const dest = path.join(assets, name.replace(/^\./, 'DOT-'));
75160
mkdirp(path.dirname(dest));
76161
fs.copyFileSync(path.join(cwd, name), dest);
77162
}
78163
});
79164

80-
/** @type {import('../types/internal.js').File[]} */
81-
const js = [];
82-
83-
for (const file of ts) {
84-
// The app.d.ts file makes TS/JS aware of some ambient modules, which are
85-
// also needed for JS projects if people turn on "checkJs" in their jsonfig
86-
if (file.name.endsWith('.d.ts')) {
87-
if (file.name.endsWith('app.d.ts')) js.push(file);
88-
} else if (file.name.endsWith('.ts')) {
89-
js.push({
90-
name: file.name.replace(/\.ts$/, '.js'),
91-
contents: convert_typescript(file.contents)
92-
});
93-
} else if (file.name.endsWith('.svelte')) {
94-
// we jump through some hoops, rather than just using svelte.preprocess,
95-
// so that the output preserves the original formatting to the extent
96-
// possible (e.g. preserving double line breaks). Sucrase is the best
97-
// tool for the job because it just removes the types; Prettier then
98-
// tidies up the end result
99-
const contents = file.contents.replace(
100-
/<script([^>]+)>([\s\S]+?)<\/script>/g,
101-
(m, attrs, typescript) => {
102-
// Sucrase assumes 'unused' imports (which _are_ used, but only
103-
// in the markup) are type imports, and strips them. This step
104-
// prevents it from drawing that conclusion
105-
const imports = [];
106-
const import_pattern = /import (.+?) from/g;
107-
let import_match;
108-
while ((import_match = import_pattern.exec(typescript))) {
109-
const word_pattern = /[a-z_$][a-z0-9_$]*/gi;
110-
let word_match;
111-
while ((word_match = word_pattern.exec(import_match[1]))) {
112-
imports.push(word_match[0]);
113-
}
114-
}
115-
116-
const suffix = `\n${imports.join(',')}`;
117-
118-
const transformed = transform(typescript + suffix, {
119-
transforms: ['typescript']
120-
}).code.slice(0, -suffix.length);
121-
122-
const contents = prettier
123-
.format(transformed, {
124-
parser: 'babel',
125-
useTabs: true,
126-
singleQuote: true,
127-
trailingComma: 'none',
128-
printWidth: 100
129-
})
130-
.trim()
131-
.replace(/^(.)/gm, '\t$1');
132-
133-
return `<script${attrs.replace(' lang="ts"', '')}>\n${contents}\n</script>`;
134-
}
135-
);
136-
137-
js.push({
138-
name: file.name,
139-
contents
140-
});
141-
} else {
142-
js.push(file);
143-
}
144-
}
145-
146165
fs.copyFileSync(meta_file, `${dir}/meta.json`);
147-
fs.writeFileSync(`${dir}/files.ts.json`, JSON.stringify(ts, null, '\t'));
148-
fs.writeFileSync(`${dir}/files.js.json`, JSON.stringify(js, null, '\t'));
166+
fs.writeFileSync(
167+
`${dir}/files.types=typescript.json`,
168+
JSON.stringify(types.typescript, null, '\t')
169+
);
170+
fs.writeFileSync(`${dir}/files.types=checkjs.json`, JSON.stringify(types.checkjs, null, '\t'));
171+
fs.writeFileSync(`${dir}/files.types=null.json`, JSON.stringify(types.null, null, '\t'));
149172
}
150173
}
151174

packages/create-svelte/scripts/update-template-repo-contents.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ await create(repo, {
1717
name: 'kit-template-default',
1818
template: 'default',
1919
eslint: false,
20-
typescript: false,
20+
types: 'checkjs',
2121
prettier: true,
2222
playwright: false
2323
});
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"extends": "./.svelte-kit/tsconfig.json",
3+
"compilerOptions": {
4+
"checkJs": true
5+
}
6+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"scripts": {
3+
"check": "svelte-check --tsconfig ./jsconfig.json",
4+
"check:watch": "svelte-check --tsconfig ./jsconfig.json --watch"
5+
},
6+
"devDependencies": {
7+
"typescript": "~4.6.2",
8+
"svelte-check": "^2.2.6"
9+
}
10+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"devDependencies": {
3+
"@types/cookie": "^0.4.1"
4+
}
5+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import adapter from '@sveltejs/adapter-auto';
2+
import preprocess from 'svelte-preprocess';
3+
4+
/** @type {import('@sveltejs/kit').Config} */
5+
const config = {
6+
// Consult https://github.com/sveltejs/svelte-preprocess
7+
// for more information about preprocessors
8+
preprocess: preprocess(),
9+
10+
kit: {
11+
adapter: adapter(),
12+
13+
// Override http methods in the Todo forms
14+
methodOverride: {
15+
allowed: ['PATCH', 'DELETE']
16+
}
17+
}
18+
};
19+
20+
export default config;

packages/create-svelte/shared/-typescript/jsconfig.json

Lines changed: 0 additions & 3 deletions
This file was deleted.

0 commit comments

Comments
 (0)