From 8b801c67de21a17e1cf058bd4d7813e1709cf23b Mon Sep 17 00:00:00 2001 From: Nick DeJesus Date: Thu, 29 May 2025 18:37:22 -0400 Subject: [PATCH 1/3] QoL changes --- README.md | 16 ++ example/shnippet.config.js | 2 + example/src/App.jsx | 95 +++++-- package/package.json | 2 +- package/src/extract/SnippetExtractor.ts | 15 +- package/src/utils/snippetManager.ts | 99 ++++--- package/test/__fixtures__/example.js | 28 +- package/test/__fixtures__/example.kt | 16 -- package/test/__fixtures__/example.ts | 4 +- package/test/__fixtures__/kotlin/example.kt | 17 ++ package/test/extract/SnippetExtractor.test.ts | 49 ++-- package/test/utils/snippetManager.test.ts | 251 ++++++++++++++++-- 12 files changed, 444 insertions(+), 150 deletions(-) delete mode 100644 package/test/__fixtures__/example.kt create mode 100644 package/test/__fixtures__/kotlin/example.kt diff --git a/README.md b/README.md index 934c768..82579da 100644 --- a/README.md +++ b/README.md @@ -272,6 +272,7 @@ snippetManager.updateConfig({ baseUrl: 'http://your-snippet-server.com/snippets', // Languages to support + // Note: The first language in this array will be used as the defaultLanguage supportedLanguages: ['python', 'kotlin', 'typescript'], // Default imports for each language @@ -284,3 +285,18 @@ snippetManager.updateConfig({ ``` The snippet manager will cache the results, making subsequent fetches instant. + +### Default Language Behavior + +The `defaultLanguage` in the returned `SnippetResult` is always set to the first language in the `supportedLanguages` array. This means: + +1. The order of languages in your `supportedLanguages` configuration determines which language is used as the default +2. This default is set regardless of whether the snippet exists in that language +3. You can control the default language by reordering the `supportedLanguages` array + +For example, if you want Kotlin to be the default language, put it first in the array: +```typescript +snippetManager.updateConfig({ + supportedLanguages: ['kotlin', 'python', 'typescript'] // Kotlin will be the default +}); +``` diff --git a/example/shnippet.config.js b/example/shnippet.config.js index b19b36c..e669a46 100644 --- a/example/shnippet.config.js +++ b/example/shnippet.config.js @@ -10,4 +10,6 @@ export const config = { prependEnd: ":prepend-end:", }, outputDirectoryStructure: "byLanguage", + baseUrl: "http://localhost:3000", + supportedLanguages: ["python", "kotlin", "javascript"] }; diff --git a/example/src/App.jsx b/example/src/App.jsx index bfb108b..5b08e51 100644 --- a/example/src/App.jsx +++ b/example/src/App.jsx @@ -1,20 +1,23 @@ -import { useState, useEffect } from "react"; -import { snippetManager } from "shnippet"; -import { config } from "../shnippet.config"; +import { useState, useEffect } from 'react'; +import { snippetManager } from 'shnippet'; +import { config } from '../shnippet.config'; // Update the snippetManager with our config snippetManager.updateConfig(config); function App() { - const [snippet, setSnippet] = useState(""); - const [error, setError] = useState(""); + const [snippetResult, setSnippetResult] = useState(null); + const [selectedLanguage, setSelectedLanguage] = useState(''); + const [error, setError] = useState(''); useEffect(() => { async function loadSnippet() { try { // Try to load a test snippet - const content = await snippetManager.getSnippet("example1", "python"); - setSnippet(content); + const result = await snippetManager.getSnippet('example1'); + setSnippetResult(result); + // Set initial language to the default language + setSelectedLanguage(result.defaultLanguage); } catch (err) { setError(err.message); } @@ -22,23 +25,81 @@ function App() { loadSnippet(); }, []); + if (error) { + return ( +
+

Shnippet Test

+
Error: {error}
+
+ ); + } + + if (!snippetResult) { + return ( +
+

Shnippet Test

+
Loading...
+
+ ); + } + return ( -
+

Shnippet Test

- {error ? ( -
Error: {error}
- ) : ( + + {/* Language Tabs */} +
+ {snippetResult.languages.map((language) => ( + + ))} +
+ + {/* Code Display */} + {snippetResult.imports?.[selectedLanguage] && ( +
+

Imports:

+
+            {snippetResult.imports[selectedLanguage].join('\n')}
+          
+
+ )} + +
+

Code:

-          {snippet || "Loading..."}
+          {snippetResult.content[selectedLanguage]}
         
- )} +
+ + {/* Imports Display */}
); } diff --git a/package/package.json b/package/package.json index 43dded6..1907501 100644 --- a/package/package.json +++ b/package/package.json @@ -1,7 +1,7 @@ { "name": "shnippet", "type": "module", - "version": "0.0.1-alpha", + "version": "0.0.2-alpha", "description": "A snippet extraction tool for various programming languages.", "main": "dist/index.js", "browser": "dist/client.browser.js", diff --git a/package/src/extract/SnippetExtractor.ts b/package/src/extract/SnippetExtractor.ts index 2f2e505..b32d712 100644 --- a/package/src/extract/SnippetExtractor.ts +++ b/package/src/extract/SnippetExtractor.ts @@ -24,6 +24,12 @@ export class SnippetExtractor { private prependBlocks: Record = {}; private projectRoot: string; private processedSnippets: Map> = new Map(); + private languageToDirectory: Record = { + python: 'py', + typescript: 'ts', + kotlin: 'kt', + javascript: 'js', + }; constructor(config: SnippetExtractorConfig) { if (typeof window !== 'undefined') { @@ -273,17 +279,18 @@ export class SnippetExtractor { private getLanguageFromExtension(extension: string): string { const extensionToLanguageMap: Record = { - '.js': 'js', + '.js': 'javascript', '.ts': 'typescript', - '.kt': 'kt', + '.kt': 'kotlin', + '.py': 'python', '.swift': 'swift', '.gradle': 'gradle', '.bash': 'bash', '.xml': 'xml', - '.py': 'python', }; - return extensionToLanguageMap[extension] || 'other'; + const language = extensionToLanguageMap[extension] || 'other'; + return this.languageToDirectory[language] || language; } public async extractSnippets(): Promise { diff --git a/package/src/utils/snippetManager.ts b/package/src/utils/snippetManager.ts index d4664d2..b8b6759 100644 --- a/package/src/utils/snippetManager.ts +++ b/package/src/utils/snippetManager.ts @@ -16,13 +16,16 @@ export interface SnippetConfig { defaultImports?: Record; } +interface SnippetResult { + name: string; + languages: string[]; + defaultLanguage: string; + imports?: Record; + content: Record; +} + interface SnippetManager { - getSnippet: (name: string, language: string) => Promise; - getSnippetDisplayInfo: (name: string) => { - languages: string[]; - defaultLanguage: string; - imports: Record; - }; + getSnippet: (name: string) => Promise; formatSnippet: ( content: string, options: { language: string; showLineNumbers?: boolean } @@ -31,8 +34,14 @@ interface SnippetManager { } class SnippetManagerImpl implements SnippetManager { - private cache: Map = new Map(); + private cache: Map = new Map(); private config: SnippetConfig; + private languageToDirectory: Record = { + python: 'py', + typescript: 'ts', + kotlin: 'kt', + javascript: 'js', + }; constructor(config: Partial = {}) { this.config = { @@ -51,36 +60,13 @@ class SnippetManagerImpl implements SnippetManager { this.cache.clear(); } - async getSnippet(name: string, language: string): Promise { - const key = `${name}-${language}`; - - if (this.cache.has(key)) { - return this.cache.get(key)!; + async getSnippet(name: string): Promise { + if (this.cache.has(name)) { + return this.cache.get(name)!; } - try { - const url = `${this.config.baseUrl}/${language}/${name}.snippet.txt`; - console.log('Fetching from:', url); - const response = await fetch(url); - console.log('Response status:', response.status); - console.log('Response type:', response.type); - - if (!response.ok) { - throw new Error(`Failed to fetch snippet: ${name} for language: ${language}`); - } - - const content = await response.text(); - console.log('Received content:', content); - this.cache.set(key, content); - return content; - } catch (error) { - console.error(`Error fetching snippet ${name} for language ${language}:`, error); - throw error; - } - } - - getSnippetDisplayInfo(name: string) { const languages = this.config.supportedLanguages || ['python', 'kotlin']; + const content: Record = {}; const imports: Record = {}; // Use configured imports or defaults @@ -89,16 +75,43 @@ class SnippetManagerImpl implements SnippetManager { kotlin: ['import java.util.*'], }; - // Add imports for each supported language - languages.forEach((lang) => { - imports[lang] = defaultImports[lang] || []; - }); + try { + // Fetch content for each language + for (const language of languages) { + try { + // Map language names to directory names + const languageDir = this.languageToDirectory[language] || language; + + const url = `${this.config.baseUrl}/${languageDir}/${name}.snippet.txt`; + const response = await fetch(url); + + if (response.ok) { + content[language] = await response.text(); + // Only add imports if they exist for this language + if (defaultImports[language]) { + imports[language] = defaultImports[language]; + } + } + } catch (error) { + console.error(`Error fetching ${language} snippet for ${name}:`, error); + } + } - return { - languages, - defaultLanguage: languages[0], - imports, - }; + const result: SnippetResult = { + name, + languages: Object.keys(content), + defaultLanguage: languages[0], + content, + // Only include imports if we have any + ...(Object.keys(imports).length > 0 && { imports }), + }; + + this.cache.set(name, result); + return result; + } catch (error) { + console.error(`Error fetching snippet ${name}:`, error); + throw error; + } } formatSnippet(content: string, options: { language: string; showLineNumbers?: boolean }): string { diff --git a/package/test/__fixtures__/example.js b/package/test/__fixtures__/example.js index c1eca25..d81be57 100644 --- a/package/test/__fixtures__/example.js +++ b/package/test/__fixtures__/example.js @@ -1,18 +1,16 @@ -// Example JavaScript file with snippets -//:snippet-start: js-hello -function sayHello() { - console.log('Hello from JavaScript!'); -} -//:snippet-end: +// :prepend-start: example1 +// JavaScript doesn't need any default imports +// :prepend-end: -//:snippet-start: js-class -class Example { - constructor() { - this.message = 'This is a JavaScript class'; - } +// :snippet-start: example1 +function hello() { + console.log("Hello World!"); + console.log("Another print"); +} +// :snippet-end: - getMessage() { - return this.message; - } +// :snippet-start: example2 +function goodbye() { + console.log("Goodbye World!"); } -//:snippet-end: +// :snippet-end: \ No newline at end of file diff --git a/package/test/__fixtures__/example.kt b/package/test/__fixtures__/example.kt deleted file mode 100644 index 1be4209..0000000 --- a/package/test/__fixtures__/example.kt +++ /dev/null @@ -1,16 +0,0 @@ -// Example Kotlin file with snippets -//:snippet-start: example1 -fun main() { - println("Hello from Kotlin!") -} -//:snippet-end: - -//:snippet-start: example2 -class Example { - private val message = "This is a Kotlin class" - - fun getMessage(): String { - return message - } -} -//:snippet-end: \ No newline at end of file diff --git a/package/test/__fixtures__/example.ts b/package/test/__fixtures__/example.ts index c48bcb6..101bbf4 100644 --- a/package/test/__fixtures__/example.ts +++ b/package/test/__fixtures__/example.ts @@ -1,11 +1,11 @@ // Example TypeScript file with snippets -//:snippet-start: ts-hello +//:snippet-start: example1 function sayHello(): void { console.log('Hello from TypeScript!'); } //:snippet-end: -//:snippet-start: ts-class +//:snippet-start: example2 class Example { private message: string; constructor() { diff --git a/package/test/__fixtures__/kotlin/example.kt b/package/test/__fixtures__/kotlin/example.kt new file mode 100644 index 0000000..829cd05 --- /dev/null +++ b/package/test/__fixtures__/kotlin/example.kt @@ -0,0 +1,17 @@ +// :prepend-start: example1 +import java.util.* + +// :prepend-end: + +// :snippet-start: example1 +fun hello() { + println("Hello World!") + println("Another print") +} +// :snippet-end: + +// :snippet-start: example2 +fun goodbye() { + println("Goodbye World!") +} +// :snippet-end: \ No newline at end of file diff --git a/package/test/extract/SnippetExtractor.test.ts b/package/test/extract/SnippetExtractor.test.ts index 597e3de..56e94a2 100644 --- a/package/test/extract/SnippetExtractor.test.ts +++ b/package/test/extract/SnippetExtractor.test.ts @@ -91,9 +91,9 @@ describe('SnippetExtractor', () => { ); const items = await fs.readdir(absoluteOutputDir); - expect(items).toContain('typescript'); + expect(items).toContain('ts'); expect(items).toContain('js'); - expect(items).toContain('python'); + expect(items).toContain('py'); expect(items).toContain('kt'); }); @@ -103,11 +103,11 @@ describe('SnippetExtractor', () => { config.snippetOutputDirectory ); - // Check JavaScript snippets - const jsDir = path.join(absoluteOutputDir, 'js'); - const jsFiles = await fs.readdir(jsDir); - expect(jsFiles).toContain('js-hello.snippet.txt'); - expect(jsFiles).toContain('js-class.snippet.txt'); + // Check TypeScript snippets + const tsDir = path.join(absoluteOutputDir, 'ts'); + const tsFiles = await fs.readdir(tsDir); + expect(tsFiles).toContain('example1.snippet.txt'); + expect(tsFiles).toContain('example2.snippet.txt'); // Check Kotlin snippets const ktDir = path.join(absoluteOutputDir, 'kt'); @@ -116,7 +116,7 @@ describe('SnippetExtractor', () => { expect(ktFiles).toContain('example2.snippet.txt'); // Check Python snippets - const pyDir = path.join(absoluteOutputDir, 'python'); + const pyDir = path.join(absoluteOutputDir, 'py'); const pyFiles = await fs.readdir(pyDir); expect(pyFiles).toContain('example1.snippet.txt'); expect(pyFiles).toContain('example2.snippet.txt'); @@ -156,39 +156,22 @@ describe('SnippetExtractor', () => { .trim(); }; - // Test JavaScript content - const jsSource = await fs.readFile( - path.join(process.cwd(), config.rootDirectory, 'example.js'), + // Test TypeScript content + const tsSource = await fs.readFile( + path.join(process.cwd(), config.rootDirectory, 'example.ts'), 'utf-8' ); - const jsSnippet = await fs.readFile( - path.join(process.cwd(), config.snippetOutputDirectory, 'js', 'js-hello.snippet.txt'), + const tsSnippet = await fs.readFile( + path.join(process.cwd(), config.snippetOutputDirectory, 'ts', 'example1.snippet.txt'), 'utf-8' ); - const jsSourceContent = extractContentFromSource( - jsSource, + const tsSourceContent = extractContentFromSource( + tsSource, config.snippetTags.start, config.snippetTags.end ); - expect(normalizeContent(jsSnippet)).toBe(normalizeContent(jsSourceContent)); - - // Test Kotlin content - const ktSource = await fs.readFile( - path.join(process.cwd(), config.rootDirectory, 'example.kt'), - 'utf-8' - ); - const ktSnippet = await fs.readFile( - path.join(process.cwd(), config.snippetOutputDirectory, 'kt', 'example1.snippet.txt'), - 'utf-8' - ); - - const ktSourceContent = extractContentFromSource( - ktSource, - config.snippetTags.start, - config.snippetTags.end - ); - expect(normalizeContent(ktSnippet)).toBe(normalizeContent(ktSourceContent)); + expect(normalizeContent(tsSnippet)).toBe(normalizeContent(tsSourceContent)); }); }); }); diff --git a/package/test/utils/snippetManager.test.ts b/package/test/utils/snippetManager.test.ts index 678469b..fb3860d 100644 --- a/package/test/utils/snippetManager.test.ts +++ b/package/test/utils/snippetManager.test.ts @@ -1,5 +1,7 @@ import { snippetManager } from '../../src/utils/snippetManager'; import { describe, expect, it, beforeEach, vi } from 'vitest'; +import path from 'path'; +import fs from 'fs/promises'; describe('SnippetManager', () => { beforeEach(() => { @@ -15,25 +17,72 @@ describe('SnippetManager', () => { }); }); - it('Can get a snippet', async () => { - const snippet = await snippetManager.getSnippet('test', 'python'); - expect(snippet).toBe("print('Hello, world!')"); + it('Can get a snippet for a single language', async () => { + const snippet = await snippetManager.getSnippet('test'); + expect(snippet.content.python).toBe("print('Hello, world!')"); }); - it('Can display info for a snippet', () => { - const displayInfo = snippetManager.getSnippetDisplayInfo('test'); - expect(displayInfo).toEqual({ - languages: ['python'], + it('Returns a complete SnippetResult object', async () => { + // Update config to support multiple languages + snippetManager.updateConfig({ + supportedLanguages: ['python', 'typescript', 'kotlin'], + }); + + // Mock fetch to return different content for each language + global.fetch = vi.fn().mockImplementation((url) => { + if (url.includes('/py/')) { + return Promise.resolve({ + ok: true, + text: () => Promise.resolve("print('Hello, world!')"), + } as Response); + } + if (url.includes('/ts/')) { + return Promise.resolve({ + ok: true, + text: () => Promise.resolve("console.log('Hello, world!')"), + } as Response); + } + if (url.includes('/kt/')) { + return Promise.resolve({ + ok: true, + text: () => Promise.resolve("println('Hello, world!')"), + } as Response); + } + return Promise.reject(new Error('Unknown language')); + }); + + const result = await snippetManager.getSnippet('example1'); + + // Test the complete structure + expect(result).toEqual({ + name: 'example1', + languages: ['python', 'typescript', 'kotlin'], defaultLanguage: 'python', + content: { + python: "print('Hello, world!')", + typescript: "console.log('Hello, world!')", + kotlin: "println('Hello, world!')", + }, imports: { python: ['from typing import Any'], + kotlin: ['import java.util.*'], }, }); + + // Test that all required properties exist + expect(result).toHaveProperty('name'); + expect(result).toHaveProperty('languages'); + expect(result).toHaveProperty('defaultLanguage'); + expect(result).toHaveProperty('content'); + expect(result).toHaveProperty('imports'); + + // Test that languages array matches content keys + expect(result.languages).toEqual(Object.keys(result.content)); }); it('Can update config and clear cache', async () => { // First, fetch a snippet to populate cache - await snippetManager.getSnippet('test', 'python'); + await snippetManager.getSnippet('test'); expect(snippetManager['cache'].size).toBe(1); // Update config with new settings @@ -55,27 +104,191 @@ describe('SnippetManager', () => { it('Caches snippet results and reuses them', async () => { // First call should make a network request - await snippetManager.getSnippet('test', 'python'); + await snippetManager.getSnippet('example1'); expect(global.fetch).toHaveBeenCalledTimes(1); // Reset the fetch mock to verify it's not called again vi.clearAllMocks(); // Second call should use cache - const snippet = await snippetManager.getSnippet('test', 'python'); - expect(snippet).toBe("print('Hello, world!')"); + const snippet = await snippetManager.getSnippet('example1'); + expect(snippet.content.python).toBe("print('Hello, world!')"); expect(global.fetch).not.toHaveBeenCalled(); }); - it('Uses different cache keys for different languages', async () => { - // Fetch same snippet in different languages - await snippetManager.getSnippet('test', 'python'); - await snippetManager.getSnippet('test', 'kotlin'); + it('Fetches all languages at once and caches them', async () => { + // Update config to support multiple languages + snippetManager.updateConfig({ + supportedLanguages: ['python', 'typescript', 'kotlin'], + }); + + // First call should make a network request for each language + const snippet = await snippetManager.getSnippet('example1'); + expect(global.fetch).toHaveBeenCalledTimes(3); + expect(snippet.content).toHaveProperty('python'); + expect(snippet.content).toHaveProperty('typescript'); + expect(snippet.content).toHaveProperty('kotlin'); + + // Reset the fetch mock + vi.clearAllMocks(); + + // Second call should use cache + const cachedSnippet = await snippetManager.getSnippet('example1'); + expect(cachedSnippet.content).toEqual(snippet.content); + expect(global.fetch).not.toHaveBeenCalled(); + + // Cache should have one entry with all languages + expect(snippetManager['cache'].size).toBe(1); + }); + + it('Uses first supported language as defaultLanguage', async () => { + // Test with different language orders + const languageOrders = [ + ['python', 'typescript', 'kotlin'], + ['kotlin', 'python', 'typescript'], + ['typescript', 'kotlin', 'python'], + ]; + + for (const languages of languageOrders) { + // Update config with new language order + snippetManager.updateConfig({ + supportedLanguages: languages, + }); + + // Get snippet and verify defaultLanguage + const result = await snippetManager.getSnippet('example1'); + expect(result.defaultLanguage).toBe(languages[0]); + } + }); + + it('Handles missing languages gracefully', async () => { + // Mock fetch to fail for typescript + global.fetch = vi.fn().mockImplementation((url) => { + if (url.includes('ts')) { + return Promise.reject(new Error('Not found')); + } + return Promise.resolve({ + ok: true, + text: () => Promise.resolve("print('Hello, world!')"), + } as Response); + }); + + snippetManager.updateConfig({ + supportedLanguages: ['python', 'typescript', 'kotlin'], + }); + + const result = await snippetManager.getSnippet('example1'); + + // Should still return a valid result with available languages + expect(result.languages).not.toContain('typescript'); + expect(result.content).not.toHaveProperty('typescript'); + expect(result.languages).toEqual(Object.keys(result.content)); + }); + + it('Handles imports correctly', async () => { + // Mock fetch to return different content for each language + global.fetch = vi.fn().mockImplementation((url) => { + if (url.includes('/py/')) { + return Promise.resolve({ + ok: true, + text: () => Promise.resolve("print('Hello, world!')"), + } as Response); + } + if (url.includes('/ts/')) { + return Promise.resolve({ + ok: true, + text: () => Promise.resolve("console.log('Hello, world!')"), + } as Response); + } + if (url.includes('/kt/')) { + return Promise.resolve({ + ok: true, + text: () => Promise.resolve("println('Hello, world!')"), + } as Response); + } + return Promise.reject(new Error('Unknown language')); + }); + + // Configure with custom imports + snippetManager.updateConfig({ + supportedLanguages: ['python', 'typescript', 'kotlin'], + defaultImports: { + python: ['from typing import Any', 'import sys'], + typescript: ['import { useState } from "react"'], + kotlin: ['import java.util.*'], + }, + }); + + const result = await snippetManager.getSnippet('example1'); + + // Verify imports are included correctly + expect(result.imports).toBeDefined(); + expect(result.imports?.python).toEqual(['from typing import Any', 'import sys']); + expect(result.imports?.typescript).toEqual(['import { useState } from "react"']); + expect(result.imports?.kotlin).toEqual(['import java.util.*']); + }); + + it('Clears cache when config changes', async () => { + // First fetch + await snippetManager.getSnippet('example1'); + expect(snippetManager['cache'].size).toBe(1); + + // Change baseUrl + snippetManager.updateConfig({ + baseUrl: 'http://new-url.com/snippets', + }); + expect(snippetManager['cache'].size).toBe(0); + + // Change supportedLanguages + await snippetManager.getSnippet('example1'); + expect(snippetManager['cache'].size).toBe(1); + snippetManager.updateConfig({ + supportedLanguages: ['kotlin', 'python'], + }); + expect(snippetManager['cache'].size).toBe(0); + + // Change defaultImports + await snippetManager.getSnippet('example1'); + expect(snippetManager['cache'].size).toBe(1); + snippetManager.updateConfig({ + defaultImports: { python: ['new import'] }, + }); + expect(snippetManager['cache'].size).toBe(0); + }); + + it('Handles language-specific content correctly', async () => { + // Mock fetch to return language-specific content + global.fetch = vi.fn().mockImplementation((url) => { + if (url.includes('/py/')) { + return Promise.resolve({ + ok: true, + text: () => Promise.resolve("print('Python content')"), + } as Response); + } + if (url.includes('/ts/')) { + return Promise.resolve({ + ok: true, + text: () => Promise.resolve("console.log('TypeScript content')"), + } as Response); + } + if (url.includes('/kt/')) { + return Promise.resolve({ + ok: true, + text: () => Promise.resolve("println('Kotlin content')"), + } as Response); + } + return Promise.reject(new Error('Unknown language')); + }); + + snippetManager.updateConfig({ + supportedLanguages: ['python', 'typescript', 'kotlin'], + }); - // Should have made two network requests - expect(global.fetch).toHaveBeenCalledTimes(2); + const result = await snippetManager.getSnippet('example1'); - // Cache should have two entries - expect(snippetManager['cache'].size).toBe(2); + // Verify each language has its specific content + expect(result.content.python).toBe("print('Python content')"); + expect(result.content.typescript).toBe("console.log('TypeScript content')"); + expect(result.content.kotlin).toBe("println('Kotlin content')"); }); }); From f6fa189fe0716869f1eb918604c0c3f2048af8ed Mon Sep 17 00:00:00 2001 From: Nick DeJesus Date: Fri, 30 May 2025 17:41:12 -0400 Subject: [PATCH 2/3] huge organizational changes, added auto gen typescript support for snippet names and updated snippetManager API --- README.md | 46 ++++++ example/src/{App.jsx => App.tsx} | 18 +-- package/build.js | 17 +-- package/package-lock.json | 182 ++++++++++++++++++++++-- package/package.json | 21 ++- package/shnippet.config.js | 14 ++ package/src/client.ts | 7 +- package/src/extract/SnippetExtractor.ts | 4 + package/src/extract/typeGenerator.ts | 48 +++++++ package/src/index.ts | 6 +- package/src/types/index.ts | 31 ++++ package/src/types/shnippet.ts | 1 + package/src/utils/snippetManager.ts | 35 +---- package/test/__fixtures__/example.js | 29 ++-- package/tsconfig.json | 9 +- 15 files changed, 377 insertions(+), 91 deletions(-) rename example/src/{App.jsx => App.tsx} (86%) create mode 100644 package/shnippet.config.js create mode 100644 package/src/extract/typeGenerator.ts create mode 100644 package/src/types/index.ts create mode 100644 package/src/types/shnippet.ts diff --git a/README.md b/README.md index 82579da..a69d775 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,52 @@ export const config = { }; ``` +## Type Safety with Generated Types + +Shnippet automatically generates TypeScript types for your snippet names. After running the extractor, you'll find a `gen-types` directory in your snippets folder containing type definitions. + +### Using Generated Types + +Import the `SnippetName` type in your TypeScript files: + +```typescript +import type { SnippetName } from '../snippets/gen-types'; + +// Now you get type safety and autocomplete for snippet names +const snippetName: SnippetName = 'example1'; // ✅ Type-safe +const invalidName: SnippetName = 'not-a-snippet'; // ❌ Type error +``` + +The generated types ensure you're using valid snippet names throughout your codebase. + +### Using with SnippetManager + +The generated types work seamlessly with `snippetManager`: + +```typescript +import { snippetManager } from 'shnippet'; +import type { SnippetName } from '../snippets/gen-types'; + +// Type-safe snippet fetching +const result = await snippetManager.getSnippet('example1' as SnippetName); + +// Type-safe in React components +function CodeExample() { + const [snippet, setSnippet] = useState(null); + + useEffect(() => { + async function loadSnippet() { + // TypeScript ensures you're using a valid snippet name + const result = await snippetManager.getSnippet('example1' as SnippetName); + setSnippet(result); + } + loadSnippet(); + }, []); + + // ... rest of component +} +``` + ## Adding Snippets to Your Test Files Mark the code you want to extract using the custom snippet tags defined in your configuration. By placing these tags in your test suites, you can directly extract code examples from your tests. diff --git a/example/src/App.jsx b/example/src/App.tsx similarity index 86% rename from example/src/App.jsx rename to example/src/App.tsx index 5b08e51..4df62bc 100644 --- a/example/src/App.jsx +++ b/example/src/App.tsx @@ -1,20 +1,24 @@ -import { useState, useEffect } from 'react'; +import React, { useState, useEffect } from 'react'; import { snippetManager } from 'shnippet'; import { config } from '../shnippet.config'; +import type { SnippetResult } from 'shnippet'; +import type { SnippetName } from '../snippets/gen-types'; + // Update the snippetManager with our config snippetManager.updateConfig(config); function App() { - const [snippetResult, setSnippetResult] = useState(null); - const [selectedLanguage, setSelectedLanguage] = useState(''); - const [error, setError] = useState(''); + const [snippetResult, setSnippetResult] = useState(null); + const [selectedLanguage, setSelectedLanguage] = useState(''); + const [error, setError] = useState(''); useEffect(() => { async function loadSnippet() { try { // Try to load a test snippet - const result = await snippetManager.getSnippet('example1'); + const result = await snippetManager.getSnippet('example1' as SnippetName); + setSnippetResult(result); // Set initial language to the default language setSelectedLanguage(result.defaultLanguage); @@ -98,10 +102,8 @@ function App() { {snippetResult.content[selectedLanguage]}
- - {/* Imports Display */} ); } -export default App; +export default App; \ No newline at end of file diff --git a/package/build.js b/package/build.js index f33b8b2..bb1d257 100644 --- a/package/build.js +++ b/package/build.js @@ -1,22 +1,14 @@ import { build } from 'esbuild'; -import { rimraf } from 'rimraf'; -// Clean dist directory -await rimraf('dist'); - -// Build Node.js bundle +// Build Node.js CLI bundle await build({ - entryPoints: ['src/index.ts', 'src/bin/cli.ts'], + entryPoints: ['src/bin/cli.ts'], bundle: true, platform: 'node', target: 'node18', format: 'esm', - outdir: 'dist', - outExtension: { '.js': '.js' }, - external: ['rimraf'], - banner: { - js: '#!/usr/bin/env node\n', - }, + outdir: 'dist/bin', + banner: { js: '#!/usr/bin/env node\n' }, sourcemap: true, minify: false, }); @@ -33,4 +25,5 @@ await build({ sourcemap: true, minify: true, globalName: 'Shnippet', + external: ['fs', 'path', 'events', 'stream', 'string_decoder'], }); diff --git a/package/package-lock.json b/package/package-lock.json index 1fd2878..ae0eec0 100644 --- a/package/package-lock.json +++ b/package/package-lock.json @@ -1,14 +1,15 @@ { "name": "shnippet", - "version": "0.0.1-alpha", + "version": "0.0.2-alpha", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "shnippet", - "version": "0.0.1-alpha", + "version": "0.0.2-alpha", "license": "MIT", "dependencies": { + "glob": "^11.0.2", "rimraf": "5.0.5" }, "bin": { @@ -1285,6 +1286,28 @@ } } }, + "node_modules/@jest/reporters/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@jest/schemas": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", @@ -3076,22 +3099,87 @@ } }, "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.2.tgz", + "integrity": "sha512-YT7U7Vye+t5fZ/QMkBFrTJ7ZQxInIUjwyAjVj84CYXqgBdv30MFUPGnBR6sQaVq6Is15wYJUsnzTuWaGRBhBAQ==", "license": "ISC", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "foreground-child": "^3.1.0", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" }, "engines": { - "node": "*" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/jackspeak": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob/node_modules/lru-cache": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", + "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob/node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -3546,6 +3634,28 @@ } } }, + "node_modules/jest-config/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/jest-diff": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", @@ -3842,6 +3952,28 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-runtime/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/jest-snapshot": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", @@ -5134,6 +5266,28 @@ "node": ">=8" } }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", diff --git a/package/package.json b/package/package.json index 1907501..83c3be5 100644 --- a/package/package.json +++ b/package/package.json @@ -3,23 +3,31 @@ "type": "module", "version": "0.0.2-alpha", "description": "A snippet extraction tool for various programming languages.", - "main": "dist/index.js", - "browser": "dist/client.browser.js", - "types": "dist/index.d.ts", + "main": "./dist/index.js", + "browser": "./dist/client.browser.js", + "types": "./dist/index.d.ts", + "include": ["src", "types"], "exports": { ".": { + "types": "./dist/index.d.ts", "import": { "node": "./dist/index.js", - "browser": "./dist/client.browser.js" + "browser": "./dist/client.browser.js", + "default": "./dist/index.js" }, - "types": "./dist/index.d.ts" + "require": "./dist/index.js" + }, + "./types": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "require": "./dist/index.js" } }, "bin": { "shnippet": "dist/bin/cli.js" }, "scripts": { - "build": "node build.js", + "build": "tsc && node build.js", "start": "node dist/src/bin/cli", "dev": "tsc --watch", "test": "vitest", @@ -38,6 +46,7 @@ "author": "Nick DeJesus", "license": "MIT", "dependencies": { + "glob": "^11.0.2", "rimraf": "5.0.5" }, "devDependencies": { diff --git a/package/shnippet.config.js b/package/shnippet.config.js new file mode 100644 index 0000000..e00ac63 --- /dev/null +++ b/package/shnippet.config.js @@ -0,0 +1,14 @@ +export const config = { + rootDirectory: './src', + snippetOutputDirectory: './snippets', + fileExtensions: ['.js', '.ts', '.kt', '.gradle', '.xml', '.bash', '.swift', '.py'], + exclude: [], + snippetTags: { + start: ':snippet-start:', + end: ':snippet-end:', + prependStart: ':prepend-start:', + prependEnd: ':prepend-end:', + }, + outputDirectoryStructure: 'byLanguage', + version: '1.0.0', +}; diff --git a/package/src/client.ts b/package/src/client.ts index 1a9023c..9c8f1f7 100644 --- a/package/src/client.ts +++ b/package/src/client.ts @@ -1 +1,6 @@ -export { snippetManager } from './utils/snippetManager'; +import { snippetManager } from './utils/snippetManager.js'; +import type { SnippetResult, SnippetConfig } from './types/index.js'; + +// Only export what's needed for browser usage +export { snippetManager }; +export type { SnippetResult, SnippetConfig }; diff --git a/package/src/extract/SnippetExtractor.ts b/package/src/extract/SnippetExtractor.ts index b32d712..3f89e8c 100644 --- a/package/src/extract/SnippetExtractor.ts +++ b/package/src/extract/SnippetExtractor.ts @@ -1,5 +1,6 @@ import path from 'path'; import { promises as fs } from 'fs'; +import { generateSnippetTypes } from './typeGenerator'; type OutputStructure = 'flat' | 'match' | 'organized' | 'byLanguage'; @@ -299,6 +300,9 @@ export class SnippetExtractor { try { await fs.mkdir(absoluteOutputDir, { recursive: true }); await this.processDirectory(this.config.rootDirectory); + + // After extracting snippets, generate types + await generateSnippetTypes(this.config.rootDirectory, this.config.snippetOutputDirectory); } catch (error) { console.error('Error extracting snippets:', error); throw error; diff --git a/package/src/extract/typeGenerator.ts b/package/src/extract/typeGenerator.ts new file mode 100644 index 0000000..af90190 --- /dev/null +++ b/package/src/extract/typeGenerator.ts @@ -0,0 +1,48 @@ +import fs from 'fs/promises'; +import path from 'path'; +import { glob } from 'glob'; + +export async function generateSnippetTypes( + rootDirectory: string, + outputDir: string +): Promise { + // Find all files that might contain snippets + const files = await glob('**/*.{js,ts,jsx,tsx,py,kt}', { + cwd: rootDirectory, + ignore: ['**/node_modules/**', '**/dist/**'], + absolute: true, + }); + + const snippets = new Set(); + + // Extract snippet names from each file + for (const file of files) { + const content = await fs.readFile(file, 'utf-8'); + const lines = content.split('\n'); + + for (const line of lines) { + const match = line.match(/:snippet-start:\s*(\w+)/); + if (match) { + snippets.add(match[1]); + } + } + } + + // Generate the types file + const typeContent = `/** + * This file is auto-generated. Do not edit manually. + * Generated from snippet tags in your codebase. + */ + +export type SnippetName = ${Array.from(snippets) + .map((s) => `'${s}'`) + .join(' | ')}; +`; + + // Create gen-types directory if it doesn't exist + const genTypesDir = path.join(outputDir, 'gen-types'); + await fs.mkdir(genTypesDir, { recursive: true }); + + // Write the types file as index.d.ts + await fs.writeFile(path.join(genTypesDir, 'index.d.ts'), typeContent, 'utf-8'); +} diff --git a/package/src/index.ts b/package/src/index.ts index 22a6a3b..1c47990 100644 --- a/package/src/index.ts +++ b/package/src/index.ts @@ -1,2 +1,6 @@ import SnippetExtractor from './extract/SnippetExtractor.js'; -export { SnippetExtractor }; +import { snippetManager } from './utils/snippetManager.js'; +import type { SnippetResult, SnippetConfig } from './types/index.js'; + +export { SnippetExtractor, snippetManager }; +export type { SnippetResult, SnippetConfig }; diff --git a/package/src/types/index.ts b/package/src/types/index.ts new file mode 100644 index 0000000..ed6f5ef --- /dev/null +++ b/package/src/types/index.ts @@ -0,0 +1,31 @@ +export interface SnippetResult { + name: string; + languages: string[]; + defaultLanguage: string; + imports?: Record; + content: Record; +} + +export interface SnippetConfig { + rootDirectory?: string; + snippetOutputDirectory?: string; + fileExtensions?: string[]; + exclude?: string[]; + snippetTags?: { + start: string; + end: string; + prependStart: string; + prependEnd: string; + }; + outputDirectoryStructure?: 'byLanguage' | 'flat'; + version?: string; + baseUrl?: string; + supportedLanguages?: string[]; + defaultImports?: Record; +} + +export interface SnippetManager { + getSnippet(name: string): Promise; + formatSnippet(content: string, options: { language: string; showLineNumbers?: boolean }): string; + updateConfig(config: Partial): void; +} diff --git a/package/src/types/shnippet.ts b/package/src/types/shnippet.ts new file mode 100644 index 0000000..0519ecb --- /dev/null +++ b/package/src/types/shnippet.ts @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/package/src/utils/snippetManager.ts b/package/src/utils/snippetManager.ts index b8b6759..34c99ba 100644 --- a/package/src/utils/snippetManager.ts +++ b/package/src/utils/snippetManager.ts @@ -1,37 +1,4 @@ -export interface SnippetConfig { - rootDirectory?: string; - snippetOutputDirectory?: string; - fileExtensions?: string[]; - exclude?: string[]; - snippetTags?: { - start: string; - end: string; - prependStart: string; - prependEnd: string; - }; - outputDirectoryStructure?: 'byLanguage' | 'flat'; - version?: string; - baseUrl?: string; - supportedLanguages?: string[]; - defaultImports?: Record; -} - -interface SnippetResult { - name: string; - languages: string[]; - defaultLanguage: string; - imports?: Record; - content: Record; -} - -interface SnippetManager { - getSnippet: (name: string) => Promise; - formatSnippet: ( - content: string, - options: { language: string; showLineNumbers?: boolean } - ) => string; - updateConfig: (config: Partial) => void; -} +import { SnippetResult, SnippetManager, SnippetConfig } from '../types/index'; class SnippetManagerImpl implements SnippetManager { private cache: Map = new Map(); diff --git a/package/test/__fixtures__/example.js b/package/test/__fixtures__/example.js index d81be57..77b8a1e 100644 --- a/package/test/__fixtures__/example.js +++ b/package/test/__fixtures__/example.js @@ -1,16 +1,17 @@ -// :prepend-start: example1 -// JavaScript doesn't need any default imports -// :prepend-end: - -// :snippet-start: example1 -function hello() { - console.log("Hello World!"); - console.log("Another print"); +"use strict"; +// Example TypeScript file with snippets +//:snippet-start: example1 +function sayHello() { + console.log('Hello from TypeScript!'); } -// :snippet-end: - -// :snippet-start: example2 -function goodbye() { - console.log("Goodbye World!"); +//:snippet-end: +//:snippet-start: example2 +class Example { + constructor() { + this.message = 'This is a TypeScript class'; + } + getMessage() { + return this.message; + } } -// :snippet-end: \ No newline at end of file +//:snippet-end: diff --git a/package/tsconfig.json b/package/tsconfig.json index 547643a..df49ae2 100644 --- a/package/tsconfig.json +++ b/package/tsconfig.json @@ -6,8 +6,15 @@ "moduleResolution": "node", "skipLibCheck": true, "strict": true, - "types": ["node"] + "types": ["node"], + "declaration": true, + "declarationDir": "./dist", + "outDir": "./dist", + "composite": true, + "declarationMap": true, + "sourceMap": true }, + "include": ["src/**/*"], "ts-node": { "esm": true } From 5e5cacb7ae44713bc79a5a002a698e49934e001a Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 30 May 2025 22:30:18 +0000 Subject: [PATCH 3/3] style: format code --- package/package.json | 5 ++++- package/shnippet.config.js | 24 ++++++++++++------------ package/src/types/shnippet.ts | 1 - package/test/__fixtures__/example.js | 16 ++++++++-------- 4 files changed, 24 insertions(+), 22 deletions(-) diff --git a/package/package.json b/package/package.json index 83c3be5..5feeca6 100644 --- a/package/package.json +++ b/package/package.json @@ -6,7 +6,10 @@ "main": "./dist/index.js", "browser": "./dist/client.browser.js", "types": "./dist/index.d.ts", - "include": ["src", "types"], + "include": [ + "src", + "types" + ], "exports": { ".": { "types": "./dist/index.d.ts", diff --git a/package/shnippet.config.js b/package/shnippet.config.js index e00ac63..f142ca8 100644 --- a/package/shnippet.config.js +++ b/package/shnippet.config.js @@ -1,14 +1,14 @@ export const config = { - rootDirectory: './src', - snippetOutputDirectory: './snippets', - fileExtensions: ['.js', '.ts', '.kt', '.gradle', '.xml', '.bash', '.swift', '.py'], - exclude: [], - snippetTags: { - start: ':snippet-start:', - end: ':snippet-end:', - prependStart: ':prepend-start:', - prependEnd: ':prepend-end:', - }, - outputDirectoryStructure: 'byLanguage', - version: '1.0.0', + rootDirectory: './src', + snippetOutputDirectory: './snippets', + fileExtensions: ['.js', '.ts', '.kt', '.gradle', '.xml', '.bash', '.swift', '.py'], + exclude: [], + snippetTags: { + start: ':snippet-start:', + end: ':snippet-end:', + prependStart: ':prepend-start:', + prependEnd: ':prepend-end:', + }, + outputDirectoryStructure: 'byLanguage', + version: '1.0.0', }; diff --git a/package/src/types/shnippet.ts b/package/src/types/shnippet.ts index 0519ecb..e69de29 100644 --- a/package/src/types/shnippet.ts +++ b/package/src/types/shnippet.ts @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/package/test/__fixtures__/example.js b/package/test/__fixtures__/example.js index 77b8a1e..8a831de 100644 --- a/package/test/__fixtures__/example.js +++ b/package/test/__fixtures__/example.js @@ -1,17 +1,17 @@ -"use strict"; +'use strict'; // Example TypeScript file with snippets //:snippet-start: example1 function sayHello() { - console.log('Hello from TypeScript!'); + console.log('Hello from TypeScript!'); } //:snippet-end: //:snippet-start: example2 class Example { - constructor() { - this.message = 'This is a TypeScript class'; - } - getMessage() { - return this.message; - } + constructor() { + this.message = 'This is a TypeScript class'; + } + getMessage() { + return this.message; + } } //:snippet-end: