diff --git a/.prettierrc b/.prettierrc
index ca0247726..06d38d393 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -5,5 +5,13 @@
"tabWidth": 2,
"semi": true,
"bracketSpacing": true,
- "plugins": ["prettier-plugin-astro"]
+ "plugins": ["prettier-plugin-astro"],
+ "overrides": [
+ {
+ "files": "*.astro",
+ "options": {
+ "parser": "astro"
+ }
+ }
+ ]
}
diff --git a/docs/tutorialkit.dev/src/components/Layout/Head.astro b/docs/tutorialkit.dev/src/components/Layout/Head.astro
index c7e64b452..882b3bb01 100644
--- a/docs/tutorialkit.dev/src/components/Layout/Head.astro
+++ b/docs/tutorialkit.dev/src/components/Layout/Head.astro
@@ -9,15 +9,25 @@ import Default from '@astrojs/starlight/components/Head.astro';
diff --git a/docs/tutorialkit.dev/tsconfig.json b/docs/tutorialkit.dev/tsconfig.json
index bfaaaef0e..b97438131 100644
--- a/docs/tutorialkit.dev/tsconfig.json
+++ b/docs/tutorialkit.dev/tsconfig.json
@@ -8,5 +8,6 @@
"jsx": "react-jsx",
"jsxImportSource": "react",
"types": ["@types/gtag.js"]
- }
+ },
+ "include": ["src"]
}
diff --git a/eslint.config.mjs b/eslint.config.mjs
index 9bd138edc..54623f4ee 100644
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -1,12 +1,13 @@
import blitzPlugin from '@blitz/eslint-plugin';
import { getNamingConventionRule, tsFileExtensions } from '@blitz/eslint-plugin/dist/configs/typescript.js';
+import eslintPluginAstro from 'eslint-plugin-astro';
export default [
{
ignores: [
'**/dist',
'**/node_modules',
- '**/.astro',
+ '**/.astro/**',
// we ignore our demo templates because they may contain code that is formatted specifically for the demo
'docs/demo/src/templates',
@@ -21,6 +22,7 @@ export default [
},
},
}),
+ ...eslintPluginAstro.configs.recommended,
{
files: ['**/env.d.ts', '**/env-default.d.ts'],
rules: {
diff --git a/extensions/vscode/build.mjs b/extensions/vscode/build.mjs
index c573a9a72..ca57d51e0 100644
--- a/extensions/vscode/build.mjs
+++ b/extensions/vscode/build.mjs
@@ -1,6 +1,6 @@
import * as esbuild from 'esbuild';
-import fs from 'node:fs';
import { execa } from 'execa';
+import fs from 'node:fs';
const production = process.argv.includes('--production');
const watch = process.argv.includes('--watch');
@@ -18,7 +18,7 @@ async function main() {
external: ['vscode'],
logLevel: 'silent',
plugins: [
- /* add to the end of plugins array */
+ // add to the end of plugins array
esbuildProblemMatcherPlugin,
],
});
@@ -33,7 +33,7 @@ async function main() {
await ctx.dispose();
if (production) {
- // rename name in package json to match extension name on store:
+ // rename name in package json to match extension name on store
const pkgJSON = JSON.parse(fs.readFileSync('./package.json', { encoding: 'utf8' }));
pkgJSON.name = 'tutorialkit';
@@ -50,14 +50,16 @@ const esbuildProblemMatcherPlugin = {
name: 'esbuild-problem-matcher',
setup(build) {
build.onStart(() => {
- console.log('[watch] build started');
+ console.log('[watch] Build started');
});
+
build.onEnd((result) => {
result.errors.forEach(({ text, location }) => {
console.error(`✘ [ERROR] ${text}`);
console.error(` ${location.file}:${location.line}:${location.column}:`);
});
- console.log('[watch] build finished');
+
+ console.log('[watch] Build finished');
});
},
};
diff --git a/extensions/vscode/src/commands/index.ts b/extensions/vscode/src/commands/index.ts
index 88409a1dd..d7447dbfb 100644
--- a/extensions/vscode/src/commands/index.ts
+++ b/extensions/vscode/src/commands/index.ts
@@ -6,10 +6,7 @@ import { selectTutorial } from './tutorialkit.select-tutorial';
import { loadTutorial } from './tutorialkit.load-tutorial';
import { initialize } from './tutorialkit.initialize';
-/**
- * No need to use these consts outside of this file:
- * – Use `cmd[name].command` instead.
- */
+// no need to use these consts outside of this file, use `cmd[name].command` instead
const CMD = {
INITIALIZE: 'tutorialkit.initialize',
SELECT_TUTORIAL: 'tutorialkit.select-tutorial',
diff --git a/extensions/vscode/src/commands/tutorialkit.initialize.ts b/extensions/vscode/src/commands/tutorialkit.initialize.ts
index eb0cbe793..b14f16e31 100644
--- a/extensions/vscode/src/commands/tutorialkit.initialize.ts
+++ b/extensions/vscode/src/commands/tutorialkit.initialize.ts
@@ -1,6 +1,6 @@
import * as vscode from 'vscode';
-import isTutorialKitWorkspace from '../utils/isTutorialKit';
import { cmd } from '.';
+import isTutorialKitWorkspace from '../utils/isTutorialKit';
export async function initialize(toastIfEmpty = false) {
const tutorialWorkpaces = (vscode.workspace.workspaceFolders || []).filter(isTutorialKitWorkspace);
@@ -10,6 +10,7 @@ export async function initialize(toastIfEmpty = false) {
vscode.window.showInformationMessage(
'No TutorialKit project found in the current workspace. Make sure there is a "@tutorialkit/astro" dependency or devDependency in your package.json file.',
);
+
vscode.commands.executeCommand('setContext', 'tutorialkit:tree', false);
}
} else if (tutorialWorkpaces.length === 1) {
diff --git a/extensions/vscode/src/commands/tutorialkit.load-tutorial.ts b/extensions/vscode/src/commands/tutorialkit.load-tutorial.ts
index 8e14218e5..3c7154df0 100644
--- a/extensions/vscode/src/commands/tutorialkit.load-tutorial.ts
+++ b/extensions/vscode/src/commands/tutorialkit.load-tutorial.ts
@@ -1,6 +1,6 @@
import * as vscode from 'vscode';
-import { LessonsTreeDataProvider, getLessonsTreeDataProvider, setLessonsTreeDataProvider } from '../views/lessonsTree';
import { extContext } from '../extension';
+import { LessonsTreeDataProvider, getLessonsTreeDataProvider, setLessonsTreeDataProvider } from '../views/lessonsTree';
export async function loadTutorial(uri: vscode.Uri) {
setLessonsTreeDataProvider(new LessonsTreeDataProvider(uri, extContext));
diff --git a/extensions/vscode/src/commands/tutorialkit.select-tutorial.ts b/extensions/vscode/src/commands/tutorialkit.select-tutorial.ts
index 860b9db46..1504e35d1 100644
--- a/extensions/vscode/src/commands/tutorialkit.select-tutorial.ts
+++ b/extensions/vscode/src/commands/tutorialkit.select-tutorial.ts
@@ -1,6 +1,6 @@
import * as vscode from 'vscode';
-import isTutorialKitWorkspace from '../utils/isTutorialKit';
import { cmd } from '.';
+import isTutorialKitWorkspace from '../utils/isTutorialKit';
export async function selectTutorial() {
const tutorialWorkpaces = (vscode.workspace.workspaceFolders || []).filter(isTutorialKitWorkspace);
diff --git a/extensions/vscode/src/utils/isTutorialKit.ts b/extensions/vscode/src/utils/isTutorialKit.ts
index d8c0d2133..3218c97ea 100644
--- a/extensions/vscode/src/utils/isTutorialKit.ts
+++ b/extensions/vscode/src/utils/isTutorialKit.ts
@@ -1,10 +1,10 @@
-import * as vscode from 'vscode';
-import * as path from 'path';
import * as fs from 'fs';
+import * as path from 'path';
+import * as vscode from 'vscode';
/**
- * Check if the workspace is a TutorialKit workspace
- * by looking for a TutorialKit dependency in the package.json file.
+ * Check if the workspace is a TutorialKit workspace by looking for a
+ * TutorialKit dependency in the package.json file.
*
* @param folder The workspace folder to check.
* @returns True if the workspace is a TutorialKit workspace, false otherwise.
@@ -13,6 +13,7 @@ export default function isTutorialKitWorkspace(folder: vscode.WorkspaceFolder):
const packageJsonPath = path.join(folder.uri.fsPath, 'package.json');
const packageJsonContent = fs.readFileSync(packageJsonPath, 'utf8');
const packageJson = JSON.parse(packageJsonContent);
+
const tutorialkitDependency =
packageJson.dependencies?.['@tutorialkit/astro'] || packageJson.devDependencies?.['@tutorialkit/astro'];
diff --git a/extensions/vscode/src/views/lessonsTree.ts b/extensions/vscode/src/views/lessonsTree.ts
index f5b342e5d..43bae2268 100644
--- a/extensions/vscode/src/views/lessonsTree.ts
+++ b/extensions/vscode/src/views/lessonsTree.ts
@@ -15,12 +15,15 @@ let lessonsTreeDataProvider: LessonsTreeDataProvider;
export function getLessonsTreeDataProvider() {
return lessonsTreeDataProvider;
}
+
export function setLessonsTreeDataProvider(provider: LessonsTreeDataProvider) {
lessonsTreeDataProvider = provider;
}
export class LessonsTreeDataProvider implements vscode.TreeDataProvider {
private _lessons: Lesson[] = [];
+ private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter();
+ readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event;
constructor(
private readonly _workspaceRoot: vscode.Uri,
@@ -59,11 +62,14 @@ export class LessonsTreeDataProvider implements vscode.TreeDataProvider
const metadataFilePath = path.join(filePath, metadataFile);
const metadataFileContent = fs.readFileSync(metadataFilePath, 'utf8');
const parsedContent = grayMatter(metadataFileContent);
+
lesson.name = parsedContent.data.title;
+
lesson.metadata = {
_path: metadataFilePath,
...(parsedContent.data as any),
};
+
lessons.push(lesson);
}
}
@@ -72,9 +78,6 @@ export class LessonsTreeDataProvider implements vscode.TreeDataProvider
return lessons;
}
- private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter();
- readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event;
-
refresh(): void {
this._loadLessons();
this._onDidChangeTreeData.fire(undefined);
diff --git a/package.json b/package.json
index b582b649a..5669d9b9c 100644
--- a/package.json
+++ b/package.json
@@ -27,6 +27,7 @@
"chalk": "^5.3.0",
"commitlint": "^19.3.0",
"conventional-changelog": "^6.0.0",
+ "eslint-plugin-astro": "^1.2.3",
"husky": "^9.0.11",
"is-ci": "^3.0.1",
"prettier": "^3.3.2",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index d67eefaef..9fd54feb8 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -29,6 +29,9 @@ importers:
conventional-changelog:
specifier: ^6.0.0
version: 6.0.0
+ eslint-plugin-astro:
+ specifier: ^1.2.3
+ version: 1.2.3(eslint@9.5.0)(typescript@5.5.3)
husky:
specifier: ^9.0.11
version: 9.0.11
@@ -3098,6 +3101,14 @@ packages:
- supports-color
dev: true
+ /@typescript-eslint/scope-manager@7.16.1:
+ resolution: {integrity: sha512-nYpyv6ALte18gbMz323RM+vpFpTjfNdyakbf3nsLvF43uF9KeNC289SUEW3QLZ1xPtyINJ1dIsZOuWuSRIWygw==}
+ engines: {node: ^18.18.0 || >=20.0.0}
+ dependencies:
+ '@typescript-eslint/types': 7.16.1
+ '@typescript-eslint/visitor-keys': 7.16.1
+ dev: true
+
/@typescript-eslint/scope-manager@8.0.0-alpha.30:
resolution: {integrity: sha512-FGW/iPWGyPFamAVZ60oCAthMqQrqafUGebF8UKuq/ha+e9SVG6YhJoRzurlQXOVf8dHfOhJ0ADMXyFnMc53clg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -3125,11 +3136,38 @@ packages:
- supports-color
dev: true
+ /@typescript-eslint/types@7.16.1:
+ resolution: {integrity: sha512-AQn9XqCzUXd4bAVEsAXM/Izk11Wx2u4H3BAfQVhSfzfDOm/wAON9nP7J5rpkCxts7E5TELmN845xTUCQrD1xIQ==}
+ engines: {node: ^18.18.0 || >=20.0.0}
+ dev: true
+
/@typescript-eslint/types@8.0.0-alpha.30:
resolution: {integrity: sha512-4WzLlw27SO9pK9UFj/Hu7WGo8WveT0SEiIpFVsV2WwtQmLps6kouwtVCB8GJPZKJyurhZhcqCoQVQFmpv441Vg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
dev: true
+ /@typescript-eslint/typescript-estree@7.16.1(typescript@5.5.3):
+ resolution: {integrity: sha512-0vFPk8tMjj6apaAZ1HlwM8w7jbghC8jc1aRNJG5vN8Ym5miyhTQGMqU++kuBFDNKe9NcPeZ6x0zfSzV8xC1UlQ==}
+ engines: {node: ^18.18.0 || >=20.0.0}
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+ dependencies:
+ '@typescript-eslint/types': 7.16.1
+ '@typescript-eslint/visitor-keys': 7.16.1
+ debug: 4.3.5
+ globby: 11.1.0
+ is-glob: 4.0.3
+ minimatch: 9.0.4
+ semver: 7.6.2
+ ts-api-utils: 1.3.0(typescript@5.5.3)
+ typescript: 5.5.3
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
/@typescript-eslint/typescript-estree@8.0.0-alpha.30(typescript@5.5.3):
resolution: {integrity: sha512-WSXbc9ZcXI+7yC+6q95u77i8FXz6HOLsw3ST+vMUlFy1lFbXyFL/3e6HDKQCm2Clt0krnoCPiTGvIn+GkYPn4Q==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -3168,6 +3206,14 @@ packages:
- typescript
dev: true
+ /@typescript-eslint/visitor-keys@7.16.1:
+ resolution: {integrity: sha512-Qlzzx4sE4u3FsHTPQAAQFJFNOuqtuY0LFrZHwQ8IHK705XxBiWOFkfKRWu6niB7hwfgnwIpO4jTC75ozW1PHWg==}
+ engines: {node: ^18.18.0 || >=20.0.0}
+ dependencies:
+ '@typescript-eslint/types': 7.16.1
+ eslint-visitor-keys: 3.4.3
+ dev: true
+
/@typescript-eslint/visitor-keys@8.0.0-alpha.30:
resolution: {integrity: sha512-XZuNurZxBqmr6ZIRIwWFq7j5RZd6ZlkId/HZEWyfciK+CWoyOxSF9Pv2VXH9Rlu2ZG2PfbhLz2Veszl4Pfn7yA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -3642,6 +3688,28 @@ packages:
resolution: {integrity: sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==}
hasBin: true
+ /astro-eslint-parser@1.0.2(typescript@5.5.3):
+ resolution: {integrity: sha512-8hJaCuqxObShWl2wEsnASqh/DbQ2O+S66m0Q3ctJlzBPEQ4pfGwwama3FCjZO3GDLQsjvn1T0v93Vxyu/+5fGw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ dependencies:
+ '@astrojs/compiler': 2.9.1
+ '@typescript-eslint/scope-manager': 7.16.1
+ '@typescript-eslint/types': 7.16.1
+ '@typescript-eslint/typescript-estree': 7.16.1(typescript@5.5.3)
+ astrojs-compiler-sync: 1.0.0(@astrojs/compiler@2.9.1)
+ debug: 4.3.5
+ entities: 4.5.0
+ eslint-scope: 8.0.1
+ eslint-visitor-keys: 4.0.0
+ espree: 10.1.0
+ globby: 11.1.0
+ is-glob: 4.0.3
+ semver: 7.6.2
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+ dev: true
+
/astro-expressive-code@0.35.3(astro@4.10.3):
resolution: {integrity: sha512-f1L1m3J3EzZHDEox6TXmuKo5fTSbaNxE/HU0S0UQmvlCowtOKnU/LOsoDwsbQSYGKz+fdLRPsCjFMiKqEoyfcw==}
peerDependencies:
@@ -3731,6 +3799,16 @@ packages:
- terser
- typescript
+ /astrojs-compiler-sync@1.0.0(@astrojs/compiler@2.9.1):
+ resolution: {integrity: sha512-IM6FxpMoBxkGGdKppkFHNQIC9Wge7jspG2MIJff8DOhG41USNJLxJfxRm7wnkTKWlYK5Y1YFFNYr2vUUKkI8sw==}
+ engines: {node: ^18.18.0 || >=20.9.0}
+ peerDependencies:
+ '@astrojs/compiler': '>=0.27.0'
+ dependencies:
+ '@astrojs/compiler': 2.9.1
+ synckit: 0.9.1
+ dev: true
+
/axobject-query@4.0.0:
resolution: {integrity: sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==}
dependencies:
@@ -4603,6 +4681,26 @@ packages:
eslint: 9.5.0
dev: true
+ /eslint-plugin-astro@1.2.3(eslint@9.5.0)(typescript@5.5.3):
+ resolution: {integrity: sha512-asHT0VUs68oppVnTHfp/WgLqs0yCx9kG9AC/PKLmp+87imeh3nGHMdFm0qP46vHxTM0NLDEhvmjFdAVAqw+QPQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: '>=8.57.0'
+ dependencies:
+ '@eslint-community/eslint-utils': 4.4.0(eslint@9.5.0)
+ '@jridgewell/sourcemap-codec': 1.4.15
+ '@typescript-eslint/types': 7.16.1
+ astro-eslint-parser: 1.0.2(typescript@5.5.3)
+ eslint: 9.5.0
+ eslint-compat-utils: 0.5.1(eslint@9.5.0)
+ globals: 15.6.0
+ postcss: 8.4.39
+ postcss-selector-parser: 6.1.0
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+ dev: true
+
/eslint-plugin-jsonc@2.16.0(eslint@9.5.0):
resolution: {integrity: sha512-Af/ZL5mgfb8FFNleH6KlO4/VdmDuTqmM+SPnWcdoWywTetv7kq+vQe99UyQb9XO3b0OWLVuTH7H0d/PXYCMdSg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -7650,6 +7748,14 @@ packages:
tslib: 2.6.3
dev: true
+ /synckit@0.9.1:
+ resolution: {integrity: sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+ dependencies:
+ '@pkgr/core': 0.1.1
+ tslib: 2.6.3
+ dev: true
+
/tar-fs@2.1.1:
resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==}
dependencies: