Skip to content

Commit

Permalink
feat: add template.visibleFiles option
Browse files Browse the repository at this point in the history
  • Loading branch information
AriPerkkio committed Jul 24, 2024
1 parent fee4b29 commit 5368ef2
Show file tree
Hide file tree
Showing 11 changed files with 95 additions and 4 deletions.
13 changes: 13 additions & 0 deletions docs/tutorialkit.dev/src/content/docs/guides/creating-content.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,19 @@ template: my-advanced-template

This declaration will make TutorialKit use the `src/templates/my-advanced-template` directory as the base for the lesson.

By default files in template are not shown in the code editor.
To make them visible, you can use `visibleFiles` option.
This can reduce repetition when you want to show same files visible in multiple lessons.

```markdown {5}
---
title: Advanced Topics
template:
name: my-advanced-template
visibleFiles: ['src/index.js', '**/utils/**']
---
```

If you start having a lot of templates and they all share some files, you can create a shared template that they all extend. This way, you can keep the shared files in one place and avoid duplication. To do that, you need to specify the `extends` property in the template's `.tk-config.json` file:

```json
Expand Down
2 changes: 2 additions & 0 deletions packages/astro/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"kleur": "4.1.5",
"mdast-util-directive": "^3.0.0",
"mdast-util-to-markdown": "^2.1.0",
"micromatch": "^4.0.7",
"nanostores": "^0.10.3",
"react": "^18.3.1",
"react-dom": "^18.3.1",
Expand All @@ -62,6 +63,7 @@
"devDependencies": {
"@tutorialkit/types": "workspace:*",
"@types/mdast": "^4.0.4",
"@types/micromatch": "^4.0.9",
"esbuild": "^0.20.2",
"esbuild-node-externals": "^1.13.1",
"execa": "^9.2.0",
Expand Down
31 changes: 29 additions & 2 deletions packages/astro/src/default/utils/content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type {
import { folderPathToFilesRef, interpolateString } from '@tutorialkit/types';
import { getCollection } from 'astro:content';
import glob from 'fast-glob';
import mm from 'micromatch';
import path from 'node:path';
import { IGNORED_FILES } from './constants';
import { DEFAULT_LOCALIZATION } from './content/default-localization';
Expand All @@ -18,6 +19,7 @@ import { logger } from './logger';
import { joinPaths } from './url';

const CONTENT_DIR = path.join(process.cwd(), 'src/content/tutorial');
const TEMPLATES_DIR = path.join(process.cwd(), 'src/templates');

export async function getTutorial(): Promise<Tutorial> {
const collection = sortCollection(await getCollection('tutorial'));
Expand Down Expand Up @@ -262,6 +264,22 @@ export async function getTutorial(): Promise<Tutorial> {
),
};

if (lesson.data.template && typeof lesson.data.template !== 'string' && lesson.data.template.visibleFiles?.length) {
const templateFilesRef = await getFilesRefList(lesson.data.template.name, TEMPLATES_DIR);

for (const filename of templateFilesRef[1]) {
if (lesson.files[1].includes(filename)) {
continue;
}

if (mm.isMatch(filename, lesson.data.template.visibleFiles, { format: formatTemplateFile })) {
lesson.files[1].push(filename);
}
}

lesson.files[1].sort();
}

if (prevLesson) {
const partSlug = _tutorial.parts[prevLesson.part.id].slug;
const chapterSlug = _tutorial.parts[prevLesson.part.id].chapters[prevLesson.chapter.id].slug;
Expand Down Expand Up @@ -330,8 +348,8 @@ function getSlug(entry: CollectionEntryTutorial) {
return slug;
}

async function getFilesRefList(pathToFolder: string): Promise<FilesRefList> {
const root = path.join(CONTENT_DIR, pathToFolder);
async function getFilesRefList(pathToFolder: string, base = CONTENT_DIR): Promise<FilesRefList> {
const root = path.join(base, pathToFolder);

const filePaths = (
await glob(`${glob.convertPathToPattern(root)}/**/*`, {
Expand All @@ -348,6 +366,15 @@ async function getFilesRefList(pathToFolder: string): Promise<FilesRefList> {
return [filesRef, filePaths];
}

function formatTemplateFile(filename: string) {
// compare files without leading "/" so that patterns like ["src/index.js"] match "/src/index.js"
if (filename.startsWith('/')) {
return filename.substring(1);
}

return filename;
}

interface CollectionEntryTutorial {
id: string;
slug: string;
Expand Down
11 changes: 10 additions & 1 deletion packages/cli/src/commands/eject/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,15 @@ interface PackageJson {
}

const TUTORIALKIT_VERSION = pkg.version;
const REQUIRED_DEPENDENCIES = ['@tutorialkit/runtime', '@webcontainer/api', 'nanostores', '@nanostores/react', 'kleur'];
const REQUIRED_DEPENDENCIES = [
'@tutorialkit/runtime',
'@webcontainer/api',
'nanostores',
'@nanostores/react',
'kleur',
'micromatch',
'@types/micromatch',
];

export function ejectRoutes(flags: Arguments) {
if (flags._[1] === 'help' || flags.help || flags.h) {
Expand Down Expand Up @@ -104,6 +112,7 @@ async function _eject(flags: EjectOptions) {
for (const dep of REQUIRED_DEPENDENCIES) {
if (!(dep in pkgJson.dependencies) && !(dep in pkgJson.devDependencies)) {
pkgJson.dependencies[dep] = astroIntegrationPkgJson.dependencies[dep];
pkgJson.devDependencies[dep] = astroIntegrationPkgJson.devDependencies[dep];

newDependencies.push(dep);
}
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/tests/__snapshots__/create-tutorial.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ exports[`create a project 1`] = `
"src/templates/default/package.json",
"src/templates/default/src",
"src/templates/default/src/index.js",
"src/templates/default/src/template-only-file.js",
"src/templates/vite-app",
"src/templates/vite-app-2",
"src/templates/vite-app-2/.tk-config.json",
Expand Down Expand Up @@ -226,6 +227,7 @@ exports[`create and eject a project 1`] = `
"src/templates/default/package.json",
"src/templates/default/src",
"src/templates/default/src/index.js",
"src/templates/default/src/template-only-file.js",
"src/templates/vite-app",
"src/templates/vite-app-2",
"src/templates/vite-app-2/.tk-config.json",
Expand Down
9 changes: 8 additions & 1 deletion packages/runtime/src/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,14 @@ export class TutorialStore {
this._lessonFiles = files;
this._lessonSolution = solution;

this._editorStore.setDocuments(files);
this._editorStore.setDocuments(
Object.fromEntries(
Object.entries({
...template,
...files,
}).filter(([filename]) => lesson.files[1].includes(filename)),
),
);

if (lesson.data.focus === undefined) {
this._editorStore.setSelectedFile(undefined);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ terminal:
panels: ['terminal', 'output']
template:
name: default
visibleFiles: ['src/template-only-file.js']
---

# Kitchen Sink [Heading 1]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default 'This file is only present in template';
10 changes: 10 additions & 0 deletions packages/types/src/schemas/common.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,16 @@ describe('webcontainerSchema', () => {
}).not.toThrow();
});
it('should allow specifying the template by object type', () => {
expect(() => {
webcontainerSchema.parse({
template: {
name: 'default',
visibleFiles: ['**/fixture.json', '*/tests/*'],
},
});
}).not.toThrow();
});
it('should allow specifying the template to omit visibleFiles', () => {
expect(() => {
webcontainerSchema.parse({
template: {
Expand Down
3 changes: 3 additions & 0 deletions packages/types/src/schemas/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,9 @@ export const webcontainerSchema = commandsSchema.extend({
z.strictObject({
// name of the template
name: z.string(),

// list of globs of files that should be visible
visibleFiles: z.array(z.string()).optional(),
}),
]),
terminal: terminalSchema.optional(),
Expand Down
16 changes: 16 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 5368ef2

Please sign in to comment.