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: