@intlayer/svelte-compiler 8.1.4 → 8.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./SvelteIntlayerCompiler.cjs`),t=require(`./svelte-intlayer-extract.cjs`);exports.ATTRIBUTES_TO_EXTRACT=t.ATTRIBUTES_TO_EXTRACT,exports.SvelteIntlayerCompiler=e.SvelteIntlayerCompiler,exports.createSvelteIntlayerCompiler=e.createSvelteIntlayerCompiler,exports.defaultShouldExtract=t.defaultShouldExtract,exports.extractDictionaryKeyFromPath=t.extractDictionaryKeyFromPath,exports.generateKey=t.generateKey,exports.intlayerSvelteExtract=t.intlayerSvelteExtract,exports.shouldProcessFile=t.shouldProcessFile,exports.svelteIntlayerCompiler=e.svelteIntlayerCompiler;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./SvelteIntlayerCompiler.cjs`),t=require(`./svelte-intlayer-extract.cjs`);exports.ATTRIBUTES_TO_EXTRACT=t.ATTRIBUTES_TO_EXTRACT,exports.SvelteIntlayerCompiler=e.SvelteIntlayerCompiler,exports.createSvelteIntlayerCompiler=e.createSvelteIntlayerCompiler,exports.defaultShouldExtract=t.defaultShouldExtract,exports.extractDictionaryKeyFromPath=t.extractDictionaryKeyFromPath,exports.intlayerSvelteExtract=t.intlayerSvelteExtract,exports.shouldProcessFile=t.shouldProcessFile,exports.svelteIntlayerCompiler=e.svelteIntlayerCompiler;
@@ -1,3 +1,3 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`node:path`),t=require(`@babel/core`);const n=[`title`,`placeholder`,`alt`,`aria-label`,`label`],r=e=>{let t=e.trim();return!(!t||!t.includes(` `)||!/^[A-Z]/.test(t)||t.startsWith(`{`)||t.startsWith(`v-`))},i=(e,t)=>{let n=e.replace(/\s+/g,` `).replace(/_+/g,` `).replace(/-+/g,` `).replace(/[^a-zA-Z0-9 ]/g,``).trim().split(` `).filter(Boolean).slice(0,5).map((e,t)=>t===0?e.toLowerCase():e.charAt(0).toUpperCase()+e.slice(1).toLowerCase()).join(``);if(n||=`content`,t.has(n)){let e=1;for(;t.has(`${n}${e}`);)e++;n=`${n}${e}`}return n},a=t=>{let n=(0,e.basename)(t,(0,e.extname)(t));return n===`index`&&(n=(0,e.basename)((0,e.dirname)(t))),`comp-${n.replace(/([a-z])([A-Z])/g,`$1-$2`).replace(/[\s_]+/g,`-`).toLowerCase()}`},o=(e,t)=>{if(!e)return!1;if(!t||t.length===0)return!0;let n=e.replace(/\\/g,`/`);return t.some(e=>e.replace(/\\/g,`/`)===n)},s=async(e,s,c={})=>{let{defaultLocale:l=`en`,packageName:u=`svelte-intlayer`,filesList:d,shouldExtract:f=r,onExtract:p}=c;if(!o(s,d)||!s.endsWith(`.svelte`))return null;let m;try{m=(await import(`magic-string`)).default}catch{return console.warn(`Svelte extraction: magic-string not found. Install it to enable Svelte content extraction.`),null}let h=new m(e),g={},_=new Set,v=a(s),y=[],b=/<script[^>]*>[\s\S]*?<\/script>/gi,x=/<style[^>]*>[\s\S]*?<\/style>/gi,S=[],C=e.matchAll(b);for(let e of C)e.index!==void 0&&S.push({start:e.index,end:e.index+e[0].length});let w=e.matchAll(x);for(let e of w)e.index!==void 0&&S.push({start:e.index,end:e.index+e[0].length});S.sort((e,t)=>e.start-t.start);let T=e=>S.some(t=>e>=t.start&&e<t.end),E=e.matchAll(/>([^<]+)</g);for(let e of E){if(e.index===void 0)continue;let t=e.index+1,n=e[1],r=t+n.length;if(!T(t)&&f(n)){let e=i(n,_);_.add(e);let a=n.replace(/\s+/g,` `).trim();y.push({start:t,end:r,replacement:`{$content.${e}}`,key:e,value:a})}}for(let t of n){let n=RegExp(`(${t})=["']([^"']+)["']`,`gi`),r=e.matchAll(n);for(let e of r){if(e.index===void 0)continue;let n=e.index,r=n+e[0].length,a=e[2];if(!T(n)&&f(a)){let e=i(a,_);_.add(e),y.push({start:n,end:r,replacement:`${t}={$content.${e}.value}`,key:e,value:a.trim()})}}}y.sort((e,t)=>t.start-e.start);for(let{start:e,end:t,replacement:n,key:r,value:i}of y)h.overwrite(e,t,n),g[r]=i;let D=/<script[^>]*>([\s\S]*?)<\/script>/.exec(e),O=!1,k=D?D[1]:``;if(D){let e=D[0].indexOf(`>`)+1,n=D.index+e;try{(0,t.traverse)((0,t.parse)(k,{parserOpts:{sourceType:`module`,plugins:[`typescript`,`jsx`]}}),{StringLiteral(e){if(e.parentPath.isImportDeclaration()||e.parentPath.isExportDeclaration()||e.parentPath.isImportSpecifier()||e.parentPath.isObjectProperty()&&e.key===`key`)return;if(e.parentPath.isCallExpression()){let n=e.parentPath.node.callee;if(t.types.isMemberExpression(n)&&t.types.isIdentifier(n.object)&&n.object.name===`console`||t.types.isIdentifier(n)&&(n.name===`useIntlayer`||n.name===`t`)||n.type===`Import`||t.types.isIdentifier(n)&&n.name===`require`)return}let r=e.node.value;if(f(r)){let t=i(r,_);_.add(t),g[t]=r.trim(),O=!0,e.node.start!=null&&e.node.end!=null&&h.overwrite(n+e.node.start,n+e.node.end,`get(content).${t}`)}}})}catch(e){console.warn(`Svelte extraction: Failed to parse script content for ${s}`,e)}}if(Object.keys(g).length===0)return null;let A=/import\s*{[^}]*useIntlayer[^}]*}\s*from\s*['"][^'"]+['"]/.test(k)||/import\s+useIntlayer\s+from\s*['"][^'"]+['"]/.test(k),j=/import\s*{[^}]*get[^}]*}\s*from\s*['"]svelte\/store['"]/.test(k),M=/const\s+content\s*=\s*useIntlayer\s*\(/.test(k);if(A&&M)return null;let N=A?``:`import { useIntlayer } from '${u}';`,P=O&&!j?`import { get } from 'svelte/store';`:``,F=M?``:`const content = useIntlayer('${v}');`,I=[N,P,F].filter(Boolean);if(I.length===0)return null;let L=`\n ${I.join(`
2
- `)}\n`;if(D){let e=D.index+D[0].indexOf(`>`)+1;h.appendLeft(e,L)}else h.prepend(`<script>\n ${N}\n ${O?`import { get } from 'svelte/store';`:``}\n ${F}\n<\/script>\n\n`);return p&&p({dictionaryKey:v,filePath:s,content:{...g},locale:l}),{code:h.toString(),map:h.generateMap({source:s,includeContent:!0}),extracted:!0}};exports.ATTRIBUTES_TO_EXTRACT=n,exports.defaultShouldExtract=r,exports.extractDictionaryKeyFromPath=a,exports.generateKey=i,exports.intlayerSvelteExtract=s,exports.shouldProcessFile=o;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`node:path`),t=require(`@babel/core`),n=require(`@intlayer/core/utils`);const r=[`title`,`placeholder`,`alt`,`aria-label`,`label`],i=e=>{let t=e.trim();return!(!t||!t.includes(` `)||!/^[A-Z]/.test(t)||t.startsWith(`{`)||t.startsWith(`v-`))},a=t=>{let n=(0,e.basename)(t,(0,e.extname)(t));return n===`index`&&(n=(0,e.basename)((0,e.dirname)(t))),`comp-${n.replace(/([a-z])([A-Z])/g,`$1-$2`).replace(/[\s_]+/g,`-`).toLowerCase()}`},o=(e,t)=>{if(!e)return!1;if(!t||t.length===0)return!0;let n=e.replace(/\\/g,`/`);return t.some(e=>e.replace(/\\/g,`/`)===n)},s=async(e,s,c={})=>{let{defaultLocale:l=`en`,packageName:u=`svelte-intlayer`,filesList:d,shouldExtract:f=i,onExtract:p}=c;if(!o(s,d)||!s.endsWith(`.svelte`))return null;let m;try{m=(await import(`magic-string`)).default}catch{return console.warn(`Svelte extraction: magic-string not found. Install it to enable Svelte content extraction.`),null}let h=new m(e),g={},_=new Set,v=a(s),y=[],b=/<script[^>]*>[\s\S]*?<\/script>/gi,x=/<style[^>]*>[\s\S]*?<\/style>/gi,S=[],C=e.matchAll(b);for(let e of C)e.index!==void 0&&S.push({start:e.index,end:e.index+e[0].length});let w=e.matchAll(x);for(let e of w)e.index!==void 0&&S.push({start:e.index,end:e.index+e[0].length});S.sort((e,t)=>e.start-t.start);let T=e=>S.some(t=>e>=t.start&&e<t.end),E=e.matchAll(/>([^<]+)</g);for(let e of E){if(e.index===void 0)continue;let t=e.index+1,r=e[1],i=t+r.length;if(!T(t)&&f(r)){let e=(0,n.generateKey)(r,_);_.add(e);let a=r.replace(/\s+/g,` `).trim();y.push({start:t,end:i,replacement:`{$content.${e}}`,key:e,value:a})}}for(let t of r){let r=RegExp(`(${t})=["']([^"']+)["']`,`gi`),i=e.matchAll(r);for(let e of i){if(e.index===void 0)continue;let r=e.index,i=r+e[0].length,a=e[2];if(!T(r)&&f(a)){let e=(0,n.generateKey)(a,_);_.add(e),y.push({start:r,end:i,replacement:`${t}={$content.${e}.value}`,key:e,value:a.trim()})}}}y.sort((e,t)=>t.start-e.start);for(let{start:e,end:t,replacement:n,key:r,value:i}of y)h.overwrite(e,t,n),g[r]=i;let D=/<script[^>]*>([\s\S]*?)<\/script>/.exec(e),O=!1,k=D?D[1]:``;if(D){let e=D[0].indexOf(`>`)+1,r=D.index+e;try{(0,t.traverse)((0,t.parse)(k,{parserOpts:{sourceType:`module`,plugins:[`typescript`,`jsx`]}}),{StringLiteral(e){if(e.parentPath.isImportDeclaration()||e.parentPath.isExportDeclaration()||e.parentPath.isImportSpecifier()||e.parentPath.isObjectProperty()&&e.key===`key`)return;if(e.parentPath.isCallExpression()){let n=e.parentPath.node.callee;if(t.types.isMemberExpression(n)&&t.types.isIdentifier(n.object)&&n.object.name===`console`||t.types.isIdentifier(n)&&(n.name===`useIntlayer`||n.name===`t`)||n.type===`Import`||t.types.isIdentifier(n)&&n.name===`require`)return}let i=e.node.value;if(f(i)){let t=(0,n.generateKey)(i,_);_.add(t),g[t]=i.trim(),O=!0,e.node.start!=null&&e.node.end!=null&&h.overwrite(r+e.node.start,r+e.node.end,`get(content).${t}`)}}})}catch(e){console.warn(`Svelte extraction: Failed to parse script content for ${s}`,e)}}if(Object.keys(g).length===0)return null;let A=/import\s*{[^}]*useIntlayer[^}]*}\s*from\s*['"][^'"]+['"]/.test(k)||/import\s+useIntlayer\s+from\s*['"][^'"]+['"]/.test(k),j=/import\s*{[^}]*get[^}]*}\s*from\s*['"]svelte\/store['"]/.test(k),M=/const\s+content\s*=\s*useIntlayer\s*\(/.test(k);if(A&&M)return null;let N=A?``:`import { useIntlayer } from '${u}';`,P=O&&!j?`import { get } from 'svelte/store';`:``,F=M?``:`const content = useIntlayer('${v}');`,I=[N,P,F].filter(Boolean);if(I.length===0)return null;let L=`\n ${I.join(`
2
+ `)}\n`;if(D){let e=D.index+D[0].indexOf(`>`)+1;h.appendLeft(e,L)}else h.prepend(`<script>\n ${N}\n ${O?`import { get } from 'svelte/store';`:``}\n ${F}\n<\/script>\n\n`);return p&&p({dictionaryKey:v,filePath:s,content:{...g},locale:l}),{code:h.toString(),map:h.generateMap({source:s,includeContent:!0}),extracted:!0}};exports.ATTRIBUTES_TO_EXTRACT=r,exports.defaultShouldExtract=i,exports.extractDictionaryKeyFromPath=a,exports.intlayerSvelteExtract=s,exports.shouldProcessFile=o;
3
3
  //# sourceMappingURL=svelte-intlayer-extract.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"svelte-intlayer-extract.cjs","names":["t"],"sources":["../../src/svelte-intlayer-extract.ts"],"sourcesContent":["import { basename, dirname, extname } from 'node:path';\nimport { parse, types as t, traverse } from '@babel/core';\n\n/* ────────────────────────────────────────── constants ───────────────────── */\n\n/**\n * Attributes that should be extracted for localization\n */\nexport const ATTRIBUTES_TO_EXTRACT = [\n 'title',\n 'placeholder',\n 'alt',\n 'aria-label',\n 'label',\n];\n\n/* ────────────────────────────────────────── types ───────────────────────── */\n\nexport type ExtractedContent = Record<string, string>;\n\n/**\n * Extracted content result from a file transformation\n */\nexport type ExtractResult = {\n /** Dictionary key derived from the file path */\n dictionaryKey: string;\n /** File path that was processed */\n filePath: string;\n /** Extracted content key-value pairs */\n content: ExtractedContent;\n /** Default locale used */\n locale: string;\n};\n\n/**\n * Options for extraction plugins\n */\nexport type ExtractPluginOptions = {\n /**\n * The default locale for the extracted content\n * @default 'en'\n */\n defaultLocale?: string;\n /**\n * The package to import useIntlayer from\n * @default 'svelte-intlayer'\n */\n packageName?: string;\n /**\n * Files list to traverse. If provided, only files in this list will be processed.\n */\n filesList?: string[];\n /**\n * Custom function to determine if a string should be extracted\n */\n shouldExtract?: (text: string) => boolean;\n /**\n * Callback function called when content is extracted from a file.\n * This allows the compiler to capture the extracted content and write it to files.\n * The dictionary will be updated: new keys added, unused keys removed.\n */\n onExtract?: (result: ExtractResult) => void;\n};\n\n/* ────────────────────────────────────────── helpers ─────────────────────── */\n\n/**\n * Default function to determine if a string should be extracted\n */\nexport const defaultShouldExtract = (text: string): boolean => {\n const trimmed = text.trim();\n if (!trimmed) return false;\n // Must contain at least one space (likely a sentence/phrase)\n if (!trimmed.includes(' ')) return false;\n // Must start with a capital letter\n if (!/^[A-Z]/.test(trimmed)) return false;\n // Filter out template logic identifiers\n if (trimmed.startsWith('{') || trimmed.startsWith('v-')) return false;\n return true;\n};\n\n/**\n * Generate a unique key from text\n */\nexport const generateKey = (\n text: string,\n existingKeys: Set<string>\n): string => {\n const maxWords = 5;\n let key = text\n .replace(/\\s+/g, ' ')\n .replace(/_+/g, ' ')\n .replace(/-+/g, ' ')\n .replace(/[^a-zA-Z0-9 ]/g, '')\n .trim()\n .split(' ')\n .filter(Boolean)\n .slice(0, maxWords)\n .map((word, index) =>\n index === 0\n ? word.toLowerCase()\n : word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()\n )\n .join('');\n\n if (!key) key = 'content';\n if (existingKeys.has(key)) {\n let i = 1;\n while (existingKeys.has(`${key}${i}`)) i++;\n key = `${key}${i}`;\n }\n return key;\n};\n\n/**\n * Extract dictionary key from file path\n */\nexport const extractDictionaryKeyFromPath = (filePath: string): string => {\n const ext = extname(filePath);\n let baseName = basename(filePath, ext);\n\n if (baseName === 'index') {\n baseName = basename(dirname(filePath));\n }\n\n // Convert to kebab-case\n const key = baseName\n .replace(/([a-z])([A-Z])/g, '$1-$2')\n .replace(/[\\s_]+/g, '-')\n .toLowerCase();\n\n return `comp-${key}`;\n};\n\n/**\n * Check if a file should be processed based on filesList\n */\nexport const shouldProcessFile = (\n filename: string | undefined,\n filesList?: string[]\n): boolean => {\n if (!filename) return false;\n if (!filesList || filesList.length === 0) return true;\n\n // Normalize paths for comparison (handle potential path separator issues)\n const normalizedFilename = filename.replace(/\\\\/g, '/');\n return filesList.some((f) => {\n const normalizedF = f.replace(/\\\\/g, '/');\n return normalizedF === normalizedFilename;\n });\n};\n\n/* ────────────────────────────────────────── MagicString type ────────────── */\n\n// MagicString type for dynamic import\ntype MagicStringType = {\n overwrite: (start: number, end: number, content: string) => void;\n appendLeft: (index: number, content: string) => void;\n prepend: (content: string) => void;\n toString: () => string;\n generateMap: (options: {\n source: string;\n includeContent: boolean;\n }) => unknown;\n};\n\n/* ────────────────────────────────────────── plugin ──────────────────────── */\n\n/**\n * Svelte extraction plugin that extracts content and transforms Svelte components to use useIntlayer.\n *\n * This plugin:\n * 1. Scans Svelte files for extractable text (template text, attributes)\n * 2. Auto-injects useIntlayer import and store binding\n * 3. Reports extracted content via onExtract callback (for the compiler to write dictionaries)\n * 4. Replaces extractable strings with content references using Svelte's reactive `$` prefix\n *\n * ## Input\n * ```svelte\n * <h1>Hello World</h1>\n * <p>Welcome to our app</p>\n * ```\n *\n * ## Output\n * ```svelte\n * <script>\n * import { useIntlayer } from 'svelte-intlayer';\n * const content = useIntlayer('hello-world');\n * </script>\n * <h1>{$content.helloWorld}</h1>\n * <p>{$content.welcomeToOurApp}</p>\n * ```\n *\n * Note: Svelte uses reactive stores with `$` prefix for automatic subscription.\n * The `useIntlayer` composable returns a Svelte store that can be accessed reactively.\n */\nexport const intlayerSvelteExtract = async (\n code: string,\n filename: string,\n options: ExtractPluginOptions = {}\n): Promise<{ code: string; map?: unknown; extracted: boolean } | null> => {\n const {\n defaultLocale = 'en',\n packageName = 'svelte-intlayer',\n filesList,\n shouldExtract = defaultShouldExtract,\n onExtract,\n } = options;\n\n // Check if file should be processed\n if (!shouldProcessFile(filename, filesList)) {\n return null;\n }\n\n // Skip non-Svelte files\n if (!filename.endsWith('.svelte')) {\n return null;\n }\n\n // Dynamic import for MagicString\n let MagicString: new (code: string) => MagicStringType;\n\n try {\n const magicStringModule = await import('magic-string');\n MagicString = magicStringModule.default;\n } catch {\n console.warn(\n 'Svelte extraction: magic-string not found. Install it to enable Svelte content extraction.'\n );\n return null;\n }\n\n const magic = new MagicString(code);\n const extractedContent: ExtractedContent = {};\n const existingKeys = new Set<string>();\n const dictionaryKey = extractDictionaryKeyFromPath(filename);\n\n // Collect all replacements first, then apply them in reverse order\n // This prevents MagicString \"chunk already edited\" errors\n type Replacement = {\n start: number;\n end: number;\n replacement: string;\n key: string;\n value: string;\n };\n const replacements: Replacement[] = [];\n\n // Extract template content (everything outside <script> and <style> tags)\n // This regex-based approach works with both TypeScript and JavaScript files\n const scriptBlockRegex = /<script[^>]*>[\\s\\S]*?<\\/script>/gi;\n const styleBlockRegex = /<style[^>]*>[\\s\\S]*?<\\/style>/gi;\n\n // Get ranges of script and style blocks to skip\n const skipRanges: Array<{ start: number; end: number }> = [];\n\n // Find all script blocks\n const scriptMatches = code.matchAll(scriptBlockRegex);\n for (const match of scriptMatches) {\n if (match.index !== undefined) {\n skipRanges.push({\n start: match.index,\n end: match.index + match[0].length,\n });\n }\n }\n\n // Find all style blocks\n const styleMatches = code.matchAll(styleBlockRegex);\n for (const match of styleMatches) {\n if (match.index !== undefined) {\n skipRanges.push({\n start: match.index,\n end: match.index + match[0].length,\n });\n }\n }\n\n // Sort ranges by start position\n skipRanges.sort((a, b) => a.start - b.start);\n\n // Function to check if a position is within a skip range\n const isInSkipRange = (pos: number): boolean => {\n return skipRanges.some((range) => pos >= range.start && pos < range.end);\n };\n\n // Extract text content between HTML tags (but not inside script/style)\n // Match text that's between > and < (tag content)\n const textContentRegex = />([^<]+)</g;\n const textMatches = code.matchAll(textContentRegex);\n for (const match of textMatches) {\n if (match.index === undefined) continue;\n\n const textStart = match.index + 1; // Skip the >\n const text = match[1];\n const textEnd = textStart + text.length;\n\n // Skip if inside script or style block\n if (isInSkipRange(textStart)) {\n continue;\n }\n\n if (shouldExtract(text)) {\n const key = generateKey(text, existingKeys);\n existingKeys.add(key);\n const normalizedValue = text.replace(/\\s+/g, ' ').trim();\n // Collect replacement instead of applying immediately\n replacements.push({\n start: textStart,\n end: textEnd,\n replacement: `{$content.${key}}`,\n key,\n value: normalizedValue,\n });\n }\n }\n\n // Extract localizable attributes (title, placeholder, alt, aria-label, label)\n for (const attrName of ATTRIBUTES_TO_EXTRACT) {\n // Match attribute=\"value\" or attribute='value'\n const attrRegex = new RegExp(`(${attrName})=[\"']([^\"']+)[\"']`, 'gi');\n const attrMatches = code.matchAll(attrRegex);\n for (const match of attrMatches) {\n if (match.index === undefined) continue;\n\n const attrStart = match.index;\n const attrEnd = attrStart + match[0].length;\n const text = match[2];\n\n // Skip if inside script or style block\n if (isInSkipRange(attrStart)) {\n continue;\n }\n\n if (shouldExtract(text)) {\n const key = generateKey(text, existingKeys);\n existingKeys.add(key);\n // Collect replacement instead of applying immediately\n replacements.push({\n start: attrStart,\n end: attrEnd,\n replacement: `${attrName}={$content.${key}.value}`,\n key,\n value: text.trim(),\n });\n }\n }\n }\n\n // Sort replacements by start position in REVERSE order (end to start)\n // This ensures earlier edits don't affect the positions of later edits\n replacements.sort((a, b) => b.start - a.start);\n\n // Apply all replacements and collect extracted content\n for (const { start, end, replacement, key, value } of replacements) {\n magic.overwrite(start, end, replacement);\n extractedContent[key] = value;\n }\n\n // Extract script content\n const scriptRegex = /<script[^>]*>([\\s\\S]*?)<\\/script>/;\n const scriptMatch = scriptRegex.exec(code);\n let hasScriptExtraction = false;\n const scriptContent = scriptMatch ? scriptMatch[1] : '';\n\n if (scriptMatch) {\n // Calculate offset: scriptMatch.index is start of <script...\n // We need to find the end of the opening tag >\n const openTagEndIndex = scriptMatch[0].indexOf('>') + 1;\n const offset = scriptMatch.index + openTagEndIndex;\n\n try {\n const ast = parse(scriptContent, {\n parserOpts: {\n sourceType: 'module',\n plugins: ['typescript', 'jsx'],\n },\n });\n\n traverse(ast, {\n StringLiteral(path) {\n if (path.parentPath.isImportDeclaration()) return;\n if (path.parentPath.isExportDeclaration()) return;\n if (path.parentPath.isImportSpecifier()) return;\n if (path.parentPath.isObjectProperty() && path.key === 'key') return;\n\n if (path.parentPath.isCallExpression()) {\n const callee = path.parentPath.node.callee;\n if (\n t.isMemberExpression(callee) &&\n t.isIdentifier(callee.object) &&\n callee.object.name === 'console'\n ) {\n return;\n }\n if (\n t.isIdentifier(callee) &&\n (callee.name === 'useIntlayer' || callee.name === 't')\n ) {\n return;\n }\n\n // Check for dynamic import import()\n if (callee.type === 'Import') return;\n\n // Check for require()\n if (t.isIdentifier(callee) && callee.name === 'require') return;\n }\n\n const text = path.node.value;\n if (shouldExtract(text)) {\n const key = generateKey(text, existingKeys);\n existingKeys.add(key);\n extractedContent[key] = text.trim();\n hasScriptExtraction = true;\n\n if (path.node.start != null && path.node.end != null) {\n magic.overwrite(\n offset + path.node.start,\n offset + path.node.end,\n `get(content).${key}`\n );\n }\n }\n },\n });\n } catch (e) {\n console.warn(\n `Svelte extraction: Failed to parse script content for ${filename}`,\n e\n );\n }\n }\n\n // If nothing was extracted, return null\n if (Object.keys(extractedContent).length === 0) {\n return null;\n }\n\n // Check if useIntlayer is already imported\n const hasUseIntlayerImport =\n /import\\s*{[^}]*useIntlayer[^}]*}\\s*from\\s*['\"][^'\"]+['\"]/.test(\n scriptContent\n ) || /import\\s+useIntlayer\\s+from\\s*['\"][^'\"]+['\"]/.test(scriptContent);\n\n // Check if get is already imported from svelte/store\n const hasGetImport =\n /import\\s*{[^}]*get[^}]*}\\s*from\\s*['\"]svelte\\/store['\"]/.test(\n scriptContent\n );\n\n // Check if content variable is already declared with useIntlayer\n const hasContentDeclaration = /const\\s+content\\s*=\\s*useIntlayer\\s*\\(/.test(\n scriptContent\n );\n\n // Skip injection if already using useIntlayer\n if (hasUseIntlayerImport && hasContentDeclaration) {\n return null;\n }\n\n // Prepare injection statements (only what's missing)\n const importStmt = hasUseIntlayerImport\n ? ''\n : `import { useIntlayer } from '${packageName}';`;\n\n const getImportStmt =\n hasScriptExtraction && !hasGetImport\n ? `import { get } from 'svelte/store';`\n : '';\n\n const contentDecl = hasContentDeclaration\n ? ''\n : `const content = useIntlayer('${dictionaryKey}');`;\n\n // Build injection string\n const injectionParts = [importStmt, getImportStmt, contentDecl].filter(\n Boolean\n );\n if (injectionParts.length === 0) {\n return null;\n }\n const injection = `\\n ${injectionParts.join('\\n ')}\\n`;\n\n if (scriptMatch) {\n // Insert at the beginning of script content\n const scriptContentStart =\n scriptMatch.index + scriptMatch[0].indexOf('>') + 1;\n magic.appendLeft(scriptContentStart, injection);\n } else {\n // No script block, create one\n magic.prepend(\n `<script>\\n ${importStmt}\\n ${hasScriptExtraction ? \"import { get } from 'svelte/store';\" : ''}\\n ${contentDecl}\\n</script>\\n\\n`\n );\n }\n\n // Call the onExtract callback with extracted content\n if (onExtract) {\n const result: ExtractResult = {\n dictionaryKey,\n filePath: filename,\n content: { ...extractedContent },\n locale: defaultLocale,\n };\n onExtract(result);\n }\n\n return {\n code: magic.toString(),\n map: magic.generateMap({ source: filename, includeContent: true }),\n extracted: true,\n };\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"svelte-intlayer-extract.cjs","names":["t"],"sources":["../../src/svelte-intlayer-extract.ts"],"sourcesContent":["import { basename, dirname, extname } from 'node:path';\nimport { parse, types as t, traverse } from '@babel/core';\nimport { generateKey } from '@intlayer/core/utils';\n\n/* ────────────────────────────────────────── constants ───────────────────── */\n\n/**\n * Attributes that should be extracted for localization\n */\nexport const ATTRIBUTES_TO_EXTRACT = [\n 'title',\n 'placeholder',\n 'alt',\n 'aria-label',\n 'label',\n];\n\n/* ────────────────────────────────────────── types ───────────────────────── */\n\nexport type ExtractedContent = Record<string, string>;\n\n/**\n * Extracted content result from a file transformation\n */\nexport type ExtractResult = {\n /** Dictionary key derived from the file path */\n dictionaryKey: string;\n /** File path that was processed */\n filePath: string;\n /** Extracted content key-value pairs */\n content: ExtractedContent;\n /** Default locale used */\n locale: string;\n};\n\n/**\n * Options for extraction plugins\n */\nexport type ExtractPluginOptions = {\n /**\n * The default locale for the extracted content\n * @default 'en'\n */\n defaultLocale?: string;\n /**\n * The package to import useIntlayer from\n * @default 'svelte-intlayer'\n */\n packageName?: string;\n /**\n * Files list to traverse. If provided, only files in this list will be processed.\n */\n filesList?: string[];\n /**\n * Custom function to determine if a string should be extracted\n */\n shouldExtract?: (text: string) => boolean;\n /**\n * Callback function called when content is extracted from a file.\n * This allows the compiler to capture the extracted content and write it to files.\n * The dictionary will be updated: new keys added, unused keys removed.\n */\n onExtract?: (result: ExtractResult) => void;\n};\n\n/* ────────────────────────────────────────── helpers ─────────────────────── */\n\n/**\n * Default function to determine if a string should be extracted\n */\nexport const defaultShouldExtract = (text: string): boolean => {\n const trimmed = text.trim();\n if (!trimmed) return false;\n // Must contain at least one space (likely a sentence/phrase)\n if (!trimmed.includes(' ')) return false;\n // Must start with a capital letter\n if (!/^[A-Z]/.test(trimmed)) return false;\n // Filter out template logic identifiers\n if (trimmed.startsWith('{') || trimmed.startsWith('v-')) return false;\n return true;\n};\n\n/**\n * Extract dictionary key from file path\n */\nexport const extractDictionaryKeyFromPath = (filePath: string): string => {\n const ext = extname(filePath);\n let baseName = basename(filePath, ext);\n\n if (baseName === 'index') {\n baseName = basename(dirname(filePath));\n }\n\n // Convert to kebab-case\n const key = baseName\n .replace(/([a-z])([A-Z])/g, '$1-$2')\n .replace(/[\\s_]+/g, '-')\n .toLowerCase();\n\n return `comp-${key}`;\n};\n\n/**\n * Check if a file should be processed based on filesList\n */\nexport const shouldProcessFile = (\n filename: string | undefined,\n filesList?: string[]\n): boolean => {\n if (!filename) return false;\n if (!filesList || filesList.length === 0) return true;\n\n // Normalize paths for comparison (handle potential path separator issues)\n const normalizedFilename = filename.replace(/\\\\/g, '/');\n return filesList.some((f) => {\n const normalizedF = f.replace(/\\\\/g, '/');\n return normalizedF === normalizedFilename;\n });\n};\n\n/* ────────────────────────────────────────── MagicString type ────────────── */\n\n// MagicString type for dynamic import\ntype MagicStringType = {\n overwrite: (start: number, end: number, content: string) => void;\n appendLeft: (index: number, content: string) => void;\n prepend: (content: string) => void;\n toString: () => string;\n generateMap: (options: {\n source: string;\n includeContent: boolean;\n }) => unknown;\n};\n\n/* ────────────────────────────────────────── plugin ──────────────────────── */\n\n/**\n * Svelte extraction plugin that extracts content and transforms Svelte components to use useIntlayer.\n *\n * This plugin:\n * 1. Scans Svelte files for extractable text (template text, attributes)\n * 2. Auto-injects useIntlayer import and store binding\n * 3. Reports extracted content via onExtract callback (for the compiler to write dictionaries)\n * 4. Replaces extractable strings with content references using Svelte's reactive `$` prefix\n *\n * ## Input\n * ```svelte\n * <h1>Hello World</h1>\n * <p>Welcome to our app</p>\n * ```\n *\n * ## Output\n * ```svelte\n * <script>\n * import { useIntlayer } from 'svelte-intlayer';\n * const content = useIntlayer('hello-world');\n * </script>\n * <h1>{$content.helloWorld}</h1>\n * <p>{$content.welcomeToOurApp}</p>\n * ```\n *\n * Note: Svelte uses reactive stores with `$` prefix for automatic subscription.\n * The `useIntlayer` composable returns a Svelte store that can be accessed reactively.\n */\nexport const intlayerSvelteExtract = async (\n code: string,\n filename: string,\n options: ExtractPluginOptions = {}\n): Promise<{ code: string; map?: unknown; extracted: boolean } | null> => {\n const {\n defaultLocale = 'en',\n packageName = 'svelte-intlayer',\n filesList,\n shouldExtract = defaultShouldExtract,\n onExtract,\n } = options;\n\n // Check if file should be processed\n if (!shouldProcessFile(filename, filesList)) {\n return null;\n }\n\n // Skip non-Svelte files\n if (!filename.endsWith('.svelte')) {\n return null;\n }\n\n // Dynamic import for MagicString\n let MagicString: new (code: string) => MagicStringType;\n\n try {\n const magicStringModule = await import('magic-string');\n MagicString = magicStringModule.default;\n } catch {\n console.warn(\n 'Svelte extraction: magic-string not found. Install it to enable Svelte content extraction.'\n );\n return null;\n }\n\n const magic = new MagicString(code);\n const extractedContent: ExtractedContent = {};\n const existingKeys = new Set<string>();\n const dictionaryKey = extractDictionaryKeyFromPath(filename);\n\n // Collect all replacements first, then apply them in reverse order\n // This prevents MagicString \"chunk already edited\" errors\n type Replacement = {\n start: number;\n end: number;\n replacement: string;\n key: string;\n value: string;\n };\n const replacements: Replacement[] = [];\n\n // Extract template content (everything outside <script> and <style> tags)\n // This regex-based approach works with both TypeScript and JavaScript files\n const scriptBlockRegex = /<script[^>]*>[\\s\\S]*?<\\/script>/gi;\n const styleBlockRegex = /<style[^>]*>[\\s\\S]*?<\\/style>/gi;\n\n // Get ranges of script and style blocks to skip\n const skipRanges: Array<{ start: number; end: number }> = [];\n\n // Find all script blocks\n const scriptMatches = code.matchAll(scriptBlockRegex);\n for (const match of scriptMatches) {\n if (match.index !== undefined) {\n skipRanges.push({\n start: match.index,\n end: match.index + match[0].length,\n });\n }\n }\n\n // Find all style blocks\n const styleMatches = code.matchAll(styleBlockRegex);\n for (const match of styleMatches) {\n if (match.index !== undefined) {\n skipRanges.push({\n start: match.index,\n end: match.index + match[0].length,\n });\n }\n }\n\n // Sort ranges by start position\n skipRanges.sort((a, b) => a.start - b.start);\n\n // Function to check if a position is within a skip range\n const isInSkipRange = (pos: number): boolean => {\n return skipRanges.some((range) => pos >= range.start && pos < range.end);\n };\n\n // Extract text content between HTML tags (but not inside script/style)\n // Match text that's between > and < (tag content)\n const textContentRegex = />([^<]+)</g;\n const textMatches = code.matchAll(textContentRegex);\n for (const match of textMatches) {\n if (match.index === undefined) continue;\n\n const textStart = match.index + 1; // Skip the >\n const text = match[1];\n const textEnd = textStart + text.length;\n\n // Skip if inside script or style block\n if (isInSkipRange(textStart)) {\n continue;\n }\n\n if (shouldExtract(text)) {\n const key = generateKey(text, existingKeys);\n existingKeys.add(key);\n const normalizedValue = text.replace(/\\s+/g, ' ').trim();\n // Collect replacement instead of applying immediately\n replacements.push({\n start: textStart,\n end: textEnd,\n replacement: `{$content.${key}}`,\n key,\n value: normalizedValue,\n });\n }\n }\n\n // Extract localizable attributes (title, placeholder, alt, aria-label, label)\n for (const attrName of ATTRIBUTES_TO_EXTRACT) {\n // Match attribute=\"value\" or attribute='value'\n const attrRegex = new RegExp(`(${attrName})=[\"']([^\"']+)[\"']`, 'gi');\n const attrMatches = code.matchAll(attrRegex);\n for (const match of attrMatches) {\n if (match.index === undefined) continue;\n\n const attrStart = match.index;\n const attrEnd = attrStart + match[0].length;\n const text = match[2];\n\n // Skip if inside script or style block\n if (isInSkipRange(attrStart)) {\n continue;\n }\n\n if (shouldExtract(text)) {\n const key = generateKey(text, existingKeys);\n existingKeys.add(key);\n // Collect replacement instead of applying immediately\n replacements.push({\n start: attrStart,\n end: attrEnd,\n replacement: `${attrName}={$content.${key}.value}`,\n key,\n value: text.trim(),\n });\n }\n }\n }\n\n // Sort replacements by start position in REVERSE order (end to start)\n // This ensures earlier edits don't affect the positions of later edits\n replacements.sort((a, b) => b.start - a.start);\n\n // Apply all replacements and collect extracted content\n for (const { start, end, replacement, key, value } of replacements) {\n magic.overwrite(start, end, replacement);\n extractedContent[key] = value;\n }\n\n // Extract script content\n const scriptRegex = /<script[^>]*>([\\s\\S]*?)<\\/script>/;\n const scriptMatch = scriptRegex.exec(code);\n let hasScriptExtraction = false;\n const scriptContent = scriptMatch ? scriptMatch[1] : '';\n\n if (scriptMatch) {\n // Calculate offset: scriptMatch.index is start of <script...\n // We need to find the end of the opening tag >\n const openTagEndIndex = scriptMatch[0].indexOf('>') + 1;\n const offset = scriptMatch.index + openTagEndIndex;\n\n try {\n const ast = parse(scriptContent, {\n parserOpts: {\n sourceType: 'module',\n plugins: ['typescript', 'jsx'],\n },\n });\n\n traverse(ast, {\n StringLiteral(path) {\n if (path.parentPath.isImportDeclaration()) return;\n if (path.parentPath.isExportDeclaration()) return;\n if (path.parentPath.isImportSpecifier()) return;\n if (path.parentPath.isObjectProperty() && path.key === 'key') return;\n\n if (path.parentPath.isCallExpression()) {\n const callee = path.parentPath.node.callee;\n if (\n t.isMemberExpression(callee) &&\n t.isIdentifier(callee.object) &&\n callee.object.name === 'console'\n ) {\n return;\n }\n if (\n t.isIdentifier(callee) &&\n (callee.name === 'useIntlayer' || callee.name === 't')\n ) {\n return;\n }\n\n // Check for dynamic import import()\n if (callee.type === 'Import') return;\n\n // Check for require()\n if (t.isIdentifier(callee) && callee.name === 'require') return;\n }\n\n const text = path.node.value;\n if (shouldExtract(text)) {\n const key = generateKey(text, existingKeys);\n existingKeys.add(key);\n extractedContent[key] = text.trim();\n hasScriptExtraction = true;\n\n if (path.node.start != null && path.node.end != null) {\n magic.overwrite(\n offset + path.node.start,\n offset + path.node.end,\n `get(content).${key}`\n );\n }\n }\n },\n });\n } catch (e) {\n console.warn(\n `Svelte extraction: Failed to parse script content for ${filename}`,\n e\n );\n }\n }\n\n // If nothing was extracted, return null\n if (Object.keys(extractedContent).length === 0) {\n return null;\n }\n\n // Check if useIntlayer is already imported\n const hasUseIntlayerImport =\n /import\\s*{[^}]*useIntlayer[^}]*}\\s*from\\s*['\"][^'\"]+['\"]/.test(\n scriptContent\n ) || /import\\s+useIntlayer\\s+from\\s*['\"][^'\"]+['\"]/.test(scriptContent);\n\n // Check if get is already imported from svelte/store\n const hasGetImport =\n /import\\s*{[^}]*get[^}]*}\\s*from\\s*['\"]svelte\\/store['\"]/.test(\n scriptContent\n );\n\n // Check if content variable is already declared with useIntlayer\n const hasContentDeclaration = /const\\s+content\\s*=\\s*useIntlayer\\s*\\(/.test(\n scriptContent\n );\n\n // Skip injection if already using useIntlayer\n if (hasUseIntlayerImport && hasContentDeclaration) {\n return null;\n }\n\n // Prepare injection statements (only what's missing)\n const importStmt = hasUseIntlayerImport\n ? ''\n : `import { useIntlayer } from '${packageName}';`;\n\n const getImportStmt =\n hasScriptExtraction && !hasGetImport\n ? `import { get } from 'svelte/store';`\n : '';\n\n const contentDecl = hasContentDeclaration\n ? ''\n : `const content = useIntlayer('${dictionaryKey}');`;\n\n // Build injection string\n const injectionParts = [importStmt, getImportStmt, contentDecl].filter(\n Boolean\n );\n if (injectionParts.length === 0) {\n return null;\n }\n const injection = `\\n ${injectionParts.join('\\n ')}\\n`;\n\n if (scriptMatch) {\n // Insert at the beginning of script content\n const scriptContentStart =\n scriptMatch.index + scriptMatch[0].indexOf('>') + 1;\n magic.appendLeft(scriptContentStart, injection);\n } else {\n // No script block, create one\n magic.prepend(\n `<script>\\n ${importStmt}\\n ${hasScriptExtraction ? \"import { get } from 'svelte/store';\" : ''}\\n ${contentDecl}\\n</script>\\n\\n`\n );\n }\n\n // Call the onExtract callback with extracted content\n if (onExtract) {\n const result: ExtractResult = {\n dictionaryKey,\n filePath: filename,\n content: { ...extractedContent },\n locale: defaultLocale,\n };\n onExtract(result);\n }\n\n return {\n code: magic.toString(),\n map: magic.generateMap({ source: filename, includeContent: true }),\n extracted: true,\n };\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- import{SvelteIntlayerCompiler as e,createSvelteIntlayerCompiler as t,svelteIntlayerCompiler as n}from"./SvelteIntlayerCompiler.mjs";import{ATTRIBUTES_TO_EXTRACT as r,defaultShouldExtract as i,extractDictionaryKeyFromPath as a,generateKey as o,intlayerSvelteExtract as s,shouldProcessFile as c}from"./svelte-intlayer-extract.mjs";export{r as ATTRIBUTES_TO_EXTRACT,e as SvelteIntlayerCompiler,t as createSvelteIntlayerCompiler,i as defaultShouldExtract,a as extractDictionaryKeyFromPath,o as generateKey,s as intlayerSvelteExtract,c as shouldProcessFile,n as svelteIntlayerCompiler};
1
+ import{SvelteIntlayerCompiler as e,createSvelteIntlayerCompiler as t,svelteIntlayerCompiler as n}from"./SvelteIntlayerCompiler.mjs";import{ATTRIBUTES_TO_EXTRACT as r,defaultShouldExtract as i,extractDictionaryKeyFromPath as a,intlayerSvelteExtract as o,shouldProcessFile as s}from"./svelte-intlayer-extract.mjs";export{r as ATTRIBUTES_TO_EXTRACT,e as SvelteIntlayerCompiler,t as createSvelteIntlayerCompiler,i as defaultShouldExtract,a as extractDictionaryKeyFromPath,o as intlayerSvelteExtract,s as shouldProcessFile,n as svelteIntlayerCompiler};
@@ -1,3 +1,3 @@
1
- import{basename as e,dirname as t,extname as n}from"node:path";import{parse as r,traverse as i,types as a}from"@babel/core";const o=[`title`,`placeholder`,`alt`,`aria-label`,`label`],s=e=>{let t=e.trim();return!(!t||!t.includes(` `)||!/^[A-Z]/.test(t)||t.startsWith(`{`)||t.startsWith(`v-`))},c=(e,t)=>{let n=e.replace(/\s+/g,` `).replace(/_+/g,` `).replace(/-+/g,` `).replace(/[^a-zA-Z0-9 ]/g,``).trim().split(` `).filter(Boolean).slice(0,5).map((e,t)=>t===0?e.toLowerCase():e.charAt(0).toUpperCase()+e.slice(1).toLowerCase()).join(``);if(n||=`content`,t.has(n)){let e=1;for(;t.has(`${n}${e}`);)e++;n=`${n}${e}`}return n},l=r=>{let i=e(r,n(r));return i===`index`&&(i=e(t(r))),`comp-${i.replace(/([a-z])([A-Z])/g,`$1-$2`).replace(/[\s_]+/g,`-`).toLowerCase()}`},u=(e,t)=>{if(!e)return!1;if(!t||t.length===0)return!0;let n=e.replace(/\\/g,`/`);return t.some(e=>e.replace(/\\/g,`/`)===n)},d=async(e,t,n={})=>{let{defaultLocale:d=`en`,packageName:f=`svelte-intlayer`,filesList:p,shouldExtract:m=s,onExtract:h}=n;if(!u(t,p)||!t.endsWith(`.svelte`))return null;let g;try{g=(await import(`magic-string`)).default}catch{return console.warn(`Svelte extraction: magic-string not found. Install it to enable Svelte content extraction.`),null}let _=new g(e),v={},y=new Set,b=l(t),x=[],S=/<script[^>]*>[\s\S]*?<\/script>/gi,C=/<style[^>]*>[\s\S]*?<\/style>/gi,w=[],T=e.matchAll(S);for(let e of T)e.index!==void 0&&w.push({start:e.index,end:e.index+e[0].length});let E=e.matchAll(C);for(let e of E)e.index!==void 0&&w.push({start:e.index,end:e.index+e[0].length});w.sort((e,t)=>e.start-t.start);let D=e=>w.some(t=>e>=t.start&&e<t.end),O=e.matchAll(/>([^<]+)</g);for(let e of O){if(e.index===void 0)continue;let t=e.index+1,n=e[1],r=t+n.length;if(!D(t)&&m(n)){let e=c(n,y);y.add(e);let i=n.replace(/\s+/g,` `).trim();x.push({start:t,end:r,replacement:`{$content.${e}}`,key:e,value:i})}}for(let t of o){let n=RegExp(`(${t})=["']([^"']+)["']`,`gi`),r=e.matchAll(n);for(let e of r){if(e.index===void 0)continue;let n=e.index,r=n+e[0].length,i=e[2];if(!D(n)&&m(i)){let e=c(i,y);y.add(e),x.push({start:n,end:r,replacement:`${t}={$content.${e}.value}`,key:e,value:i.trim()})}}}x.sort((e,t)=>t.start-e.start);for(let{start:e,end:t,replacement:n,key:r,value:i}of x)_.overwrite(e,t,n),v[r]=i;let k=/<script[^>]*>([\s\S]*?)<\/script>/.exec(e),A=!1,j=k?k[1]:``;if(k){let e=k[0].indexOf(`>`)+1,n=k.index+e;try{i(r(j,{parserOpts:{sourceType:`module`,plugins:[`typescript`,`jsx`]}}),{StringLiteral(e){if(e.parentPath.isImportDeclaration()||e.parentPath.isExportDeclaration()||e.parentPath.isImportSpecifier()||e.parentPath.isObjectProperty()&&e.key===`key`)return;if(e.parentPath.isCallExpression()){let t=e.parentPath.node.callee;if(a.isMemberExpression(t)&&a.isIdentifier(t.object)&&t.object.name===`console`||a.isIdentifier(t)&&(t.name===`useIntlayer`||t.name===`t`)||t.type===`Import`||a.isIdentifier(t)&&t.name===`require`)return}let t=e.node.value;if(m(t)){let r=c(t,y);y.add(r),v[r]=t.trim(),A=!0,e.node.start!=null&&e.node.end!=null&&_.overwrite(n+e.node.start,n+e.node.end,`get(content).${r}`)}}})}catch(e){console.warn(`Svelte extraction: Failed to parse script content for ${t}`,e)}}if(Object.keys(v).length===0)return null;let M=/import\s*{[^}]*useIntlayer[^}]*}\s*from\s*['"][^'"]+['"]/.test(j)||/import\s+useIntlayer\s+from\s*['"][^'"]+['"]/.test(j),N=/import\s*{[^}]*get[^}]*}\s*from\s*['"]svelte\/store['"]/.test(j),P=/const\s+content\s*=\s*useIntlayer\s*\(/.test(j);if(M&&P)return null;let F=M?``:`import { useIntlayer } from '${f}';`,I=A&&!N?`import { get } from 'svelte/store';`:``,L=P?``:`const content = useIntlayer('${b}');`,R=[F,I,L].filter(Boolean);if(R.length===0)return null;let z=`\n ${R.join(`
2
- `)}\n`;if(k){let e=k.index+k[0].indexOf(`>`)+1;_.appendLeft(e,z)}else _.prepend(`<script>\n ${F}\n ${A?`import { get } from 'svelte/store';`:``}\n ${L}\n<\/script>\n\n`);return h&&h({dictionaryKey:b,filePath:t,content:{...v},locale:d}),{code:_.toString(),map:_.generateMap({source:t,includeContent:!0}),extracted:!0}};export{o as ATTRIBUTES_TO_EXTRACT,s as defaultShouldExtract,l as extractDictionaryKeyFromPath,c as generateKey,d as intlayerSvelteExtract,u as shouldProcessFile};
1
+ import{basename as e,dirname as t,extname as n}from"node:path";import{parse as r,traverse as i,types as a}from"@babel/core";import{generateKey as o}from"@intlayer/core/utils";const s=[`title`,`placeholder`,`alt`,`aria-label`,`label`],c=e=>{let t=e.trim();return!(!t||!t.includes(` `)||!/^[A-Z]/.test(t)||t.startsWith(`{`)||t.startsWith(`v-`))},l=r=>{let i=e(r,n(r));return i===`index`&&(i=e(t(r))),`comp-${i.replace(/([a-z])([A-Z])/g,`$1-$2`).replace(/[\s_]+/g,`-`).toLowerCase()}`},u=(e,t)=>{if(!e)return!1;if(!t||t.length===0)return!0;let n=e.replace(/\\/g,`/`);return t.some(e=>e.replace(/\\/g,`/`)===n)},d=async(e,t,n={})=>{let{defaultLocale:d=`en`,packageName:f=`svelte-intlayer`,filesList:p,shouldExtract:m=c,onExtract:h}=n;if(!u(t,p)||!t.endsWith(`.svelte`))return null;let g;try{g=(await import(`magic-string`)).default}catch{return console.warn(`Svelte extraction: magic-string not found. Install it to enable Svelte content extraction.`),null}let _=new g(e),v={},y=new Set,b=l(t),x=[],S=/<script[^>]*>[\s\S]*?<\/script>/gi,C=/<style[^>]*>[\s\S]*?<\/style>/gi,w=[],T=e.matchAll(S);for(let e of T)e.index!==void 0&&w.push({start:e.index,end:e.index+e[0].length});let E=e.matchAll(C);for(let e of E)e.index!==void 0&&w.push({start:e.index,end:e.index+e[0].length});w.sort((e,t)=>e.start-t.start);let D=e=>w.some(t=>e>=t.start&&e<t.end),O=e.matchAll(/>([^<]+)</g);for(let e of O){if(e.index===void 0)continue;let t=e.index+1,n=e[1],r=t+n.length;if(!D(t)&&m(n)){let e=o(n,y);y.add(e);let i=n.replace(/\s+/g,` `).trim();x.push({start:t,end:r,replacement:`{$content.${e}}`,key:e,value:i})}}for(let t of s){let n=RegExp(`(${t})=["']([^"']+)["']`,`gi`),r=e.matchAll(n);for(let e of r){if(e.index===void 0)continue;let n=e.index,r=n+e[0].length,i=e[2];if(!D(n)&&m(i)){let e=o(i,y);y.add(e),x.push({start:n,end:r,replacement:`${t}={$content.${e}.value}`,key:e,value:i.trim()})}}}x.sort((e,t)=>t.start-e.start);for(let{start:e,end:t,replacement:n,key:r,value:i}of x)_.overwrite(e,t,n),v[r]=i;let k=/<script[^>]*>([\s\S]*?)<\/script>/.exec(e),A=!1,j=k?k[1]:``;if(k){let e=k[0].indexOf(`>`)+1,n=k.index+e;try{i(r(j,{parserOpts:{sourceType:`module`,plugins:[`typescript`,`jsx`]}}),{StringLiteral(e){if(e.parentPath.isImportDeclaration()||e.parentPath.isExportDeclaration()||e.parentPath.isImportSpecifier()||e.parentPath.isObjectProperty()&&e.key===`key`)return;if(e.parentPath.isCallExpression()){let t=e.parentPath.node.callee;if(a.isMemberExpression(t)&&a.isIdentifier(t.object)&&t.object.name===`console`||a.isIdentifier(t)&&(t.name===`useIntlayer`||t.name===`t`)||t.type===`Import`||a.isIdentifier(t)&&t.name===`require`)return}let t=e.node.value;if(m(t)){let r=o(t,y);y.add(r),v[r]=t.trim(),A=!0,e.node.start!=null&&e.node.end!=null&&_.overwrite(n+e.node.start,n+e.node.end,`get(content).${r}`)}}})}catch(e){console.warn(`Svelte extraction: Failed to parse script content for ${t}`,e)}}if(Object.keys(v).length===0)return null;let M=/import\s*{[^}]*useIntlayer[^}]*}\s*from\s*['"][^'"]+['"]/.test(j)||/import\s+useIntlayer\s+from\s*['"][^'"]+['"]/.test(j),N=/import\s*{[^}]*get[^}]*}\s*from\s*['"]svelte\/store['"]/.test(j),P=/const\s+content\s*=\s*useIntlayer\s*\(/.test(j);if(M&&P)return null;let F=M?``:`import { useIntlayer } from '${f}';`,I=A&&!N?`import { get } from 'svelte/store';`:``,L=P?``:`const content = useIntlayer('${b}');`,R=[F,I,L].filter(Boolean);if(R.length===0)return null;let z=`\n ${R.join(`
2
+ `)}\n`;if(k){let e=k.index+k[0].indexOf(`>`)+1;_.appendLeft(e,z)}else _.prepend(`<script>\n ${F}\n ${A?`import { get } from 'svelte/store';`:``}\n ${L}\n<\/script>\n\n`);return h&&h({dictionaryKey:b,filePath:t,content:{...v},locale:d}),{code:_.toString(),map:_.generateMap({source:t,includeContent:!0}),extracted:!0}};export{s as ATTRIBUTES_TO_EXTRACT,c as defaultShouldExtract,l as extractDictionaryKeyFromPath,d as intlayerSvelteExtract,u as shouldProcessFile};
3
3
  //# sourceMappingURL=svelte-intlayer-extract.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"svelte-intlayer-extract.mjs","names":["t"],"sources":["../../src/svelte-intlayer-extract.ts"],"sourcesContent":["import { basename, dirname, extname } from 'node:path';\nimport { parse, types as t, traverse } from '@babel/core';\n\n/* ────────────────────────────────────────── constants ───────────────────── */\n\n/**\n * Attributes that should be extracted for localization\n */\nexport const ATTRIBUTES_TO_EXTRACT = [\n 'title',\n 'placeholder',\n 'alt',\n 'aria-label',\n 'label',\n];\n\n/* ────────────────────────────────────────── types ───────────────────────── */\n\nexport type ExtractedContent = Record<string, string>;\n\n/**\n * Extracted content result from a file transformation\n */\nexport type ExtractResult = {\n /** Dictionary key derived from the file path */\n dictionaryKey: string;\n /** File path that was processed */\n filePath: string;\n /** Extracted content key-value pairs */\n content: ExtractedContent;\n /** Default locale used */\n locale: string;\n};\n\n/**\n * Options for extraction plugins\n */\nexport type ExtractPluginOptions = {\n /**\n * The default locale for the extracted content\n * @default 'en'\n */\n defaultLocale?: string;\n /**\n * The package to import useIntlayer from\n * @default 'svelte-intlayer'\n */\n packageName?: string;\n /**\n * Files list to traverse. If provided, only files in this list will be processed.\n */\n filesList?: string[];\n /**\n * Custom function to determine if a string should be extracted\n */\n shouldExtract?: (text: string) => boolean;\n /**\n * Callback function called when content is extracted from a file.\n * This allows the compiler to capture the extracted content and write it to files.\n * The dictionary will be updated: new keys added, unused keys removed.\n */\n onExtract?: (result: ExtractResult) => void;\n};\n\n/* ────────────────────────────────────────── helpers ─────────────────────── */\n\n/**\n * Default function to determine if a string should be extracted\n */\nexport const defaultShouldExtract = (text: string): boolean => {\n const trimmed = text.trim();\n if (!trimmed) return false;\n // Must contain at least one space (likely a sentence/phrase)\n if (!trimmed.includes(' ')) return false;\n // Must start with a capital letter\n if (!/^[A-Z]/.test(trimmed)) return false;\n // Filter out template logic identifiers\n if (trimmed.startsWith('{') || trimmed.startsWith('v-')) return false;\n return true;\n};\n\n/**\n * Generate a unique key from text\n */\nexport const generateKey = (\n text: string,\n existingKeys: Set<string>\n): string => {\n const maxWords = 5;\n let key = text\n .replace(/\\s+/g, ' ')\n .replace(/_+/g, ' ')\n .replace(/-+/g, ' ')\n .replace(/[^a-zA-Z0-9 ]/g, '')\n .trim()\n .split(' ')\n .filter(Boolean)\n .slice(0, maxWords)\n .map((word, index) =>\n index === 0\n ? word.toLowerCase()\n : word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()\n )\n .join('');\n\n if (!key) key = 'content';\n if (existingKeys.has(key)) {\n let i = 1;\n while (existingKeys.has(`${key}${i}`)) i++;\n key = `${key}${i}`;\n }\n return key;\n};\n\n/**\n * Extract dictionary key from file path\n */\nexport const extractDictionaryKeyFromPath = (filePath: string): string => {\n const ext = extname(filePath);\n let baseName = basename(filePath, ext);\n\n if (baseName === 'index') {\n baseName = basename(dirname(filePath));\n }\n\n // Convert to kebab-case\n const key = baseName\n .replace(/([a-z])([A-Z])/g, '$1-$2')\n .replace(/[\\s_]+/g, '-')\n .toLowerCase();\n\n return `comp-${key}`;\n};\n\n/**\n * Check if a file should be processed based on filesList\n */\nexport const shouldProcessFile = (\n filename: string | undefined,\n filesList?: string[]\n): boolean => {\n if (!filename) return false;\n if (!filesList || filesList.length === 0) return true;\n\n // Normalize paths for comparison (handle potential path separator issues)\n const normalizedFilename = filename.replace(/\\\\/g, '/');\n return filesList.some((f) => {\n const normalizedF = f.replace(/\\\\/g, '/');\n return normalizedF === normalizedFilename;\n });\n};\n\n/* ────────────────────────────────────────── MagicString type ────────────── */\n\n// MagicString type for dynamic import\ntype MagicStringType = {\n overwrite: (start: number, end: number, content: string) => void;\n appendLeft: (index: number, content: string) => void;\n prepend: (content: string) => void;\n toString: () => string;\n generateMap: (options: {\n source: string;\n includeContent: boolean;\n }) => unknown;\n};\n\n/* ────────────────────────────────────────── plugin ──────────────────────── */\n\n/**\n * Svelte extraction plugin that extracts content and transforms Svelte components to use useIntlayer.\n *\n * This plugin:\n * 1. Scans Svelte files for extractable text (template text, attributes)\n * 2. Auto-injects useIntlayer import and store binding\n * 3. Reports extracted content via onExtract callback (for the compiler to write dictionaries)\n * 4. Replaces extractable strings with content references using Svelte's reactive `$` prefix\n *\n * ## Input\n * ```svelte\n * <h1>Hello World</h1>\n * <p>Welcome to our app</p>\n * ```\n *\n * ## Output\n * ```svelte\n * <script>\n * import { useIntlayer } from 'svelte-intlayer';\n * const content = useIntlayer('hello-world');\n * </script>\n * <h1>{$content.helloWorld}</h1>\n * <p>{$content.welcomeToOurApp}</p>\n * ```\n *\n * Note: Svelte uses reactive stores with `$` prefix for automatic subscription.\n * The `useIntlayer` composable returns a Svelte store that can be accessed reactively.\n */\nexport const intlayerSvelteExtract = async (\n code: string,\n filename: string,\n options: ExtractPluginOptions = {}\n): Promise<{ code: string; map?: unknown; extracted: boolean } | null> => {\n const {\n defaultLocale = 'en',\n packageName = 'svelte-intlayer',\n filesList,\n shouldExtract = defaultShouldExtract,\n onExtract,\n } = options;\n\n // Check if file should be processed\n if (!shouldProcessFile(filename, filesList)) {\n return null;\n }\n\n // Skip non-Svelte files\n if (!filename.endsWith('.svelte')) {\n return null;\n }\n\n // Dynamic import for MagicString\n let MagicString: new (code: string) => MagicStringType;\n\n try {\n const magicStringModule = await import('magic-string');\n MagicString = magicStringModule.default;\n } catch {\n console.warn(\n 'Svelte extraction: magic-string not found. Install it to enable Svelte content extraction.'\n );\n return null;\n }\n\n const magic = new MagicString(code);\n const extractedContent: ExtractedContent = {};\n const existingKeys = new Set<string>();\n const dictionaryKey = extractDictionaryKeyFromPath(filename);\n\n // Collect all replacements first, then apply them in reverse order\n // This prevents MagicString \"chunk already edited\" errors\n type Replacement = {\n start: number;\n end: number;\n replacement: string;\n key: string;\n value: string;\n };\n const replacements: Replacement[] = [];\n\n // Extract template content (everything outside <script> and <style> tags)\n // This regex-based approach works with both TypeScript and JavaScript files\n const scriptBlockRegex = /<script[^>]*>[\\s\\S]*?<\\/script>/gi;\n const styleBlockRegex = /<style[^>]*>[\\s\\S]*?<\\/style>/gi;\n\n // Get ranges of script and style blocks to skip\n const skipRanges: Array<{ start: number; end: number }> = [];\n\n // Find all script blocks\n const scriptMatches = code.matchAll(scriptBlockRegex);\n for (const match of scriptMatches) {\n if (match.index !== undefined) {\n skipRanges.push({\n start: match.index,\n end: match.index + match[0].length,\n });\n }\n }\n\n // Find all style blocks\n const styleMatches = code.matchAll(styleBlockRegex);\n for (const match of styleMatches) {\n if (match.index !== undefined) {\n skipRanges.push({\n start: match.index,\n end: match.index + match[0].length,\n });\n }\n }\n\n // Sort ranges by start position\n skipRanges.sort((a, b) => a.start - b.start);\n\n // Function to check if a position is within a skip range\n const isInSkipRange = (pos: number): boolean => {\n return skipRanges.some((range) => pos >= range.start && pos < range.end);\n };\n\n // Extract text content between HTML tags (but not inside script/style)\n // Match text that's between > and < (tag content)\n const textContentRegex = />([^<]+)</g;\n const textMatches = code.matchAll(textContentRegex);\n for (const match of textMatches) {\n if (match.index === undefined) continue;\n\n const textStart = match.index + 1; // Skip the >\n const text = match[1];\n const textEnd = textStart + text.length;\n\n // Skip if inside script or style block\n if (isInSkipRange(textStart)) {\n continue;\n }\n\n if (shouldExtract(text)) {\n const key = generateKey(text, existingKeys);\n existingKeys.add(key);\n const normalizedValue = text.replace(/\\s+/g, ' ').trim();\n // Collect replacement instead of applying immediately\n replacements.push({\n start: textStart,\n end: textEnd,\n replacement: `{$content.${key}}`,\n key,\n value: normalizedValue,\n });\n }\n }\n\n // Extract localizable attributes (title, placeholder, alt, aria-label, label)\n for (const attrName of ATTRIBUTES_TO_EXTRACT) {\n // Match attribute=\"value\" or attribute='value'\n const attrRegex = new RegExp(`(${attrName})=[\"']([^\"']+)[\"']`, 'gi');\n const attrMatches = code.matchAll(attrRegex);\n for (const match of attrMatches) {\n if (match.index === undefined) continue;\n\n const attrStart = match.index;\n const attrEnd = attrStart + match[0].length;\n const text = match[2];\n\n // Skip if inside script or style block\n if (isInSkipRange(attrStart)) {\n continue;\n }\n\n if (shouldExtract(text)) {\n const key = generateKey(text, existingKeys);\n existingKeys.add(key);\n // Collect replacement instead of applying immediately\n replacements.push({\n start: attrStart,\n end: attrEnd,\n replacement: `${attrName}={$content.${key}.value}`,\n key,\n value: text.trim(),\n });\n }\n }\n }\n\n // Sort replacements by start position in REVERSE order (end to start)\n // This ensures earlier edits don't affect the positions of later edits\n replacements.sort((a, b) => b.start - a.start);\n\n // Apply all replacements and collect extracted content\n for (const { start, end, replacement, key, value } of replacements) {\n magic.overwrite(start, end, replacement);\n extractedContent[key] = value;\n }\n\n // Extract script content\n const scriptRegex = /<script[^>]*>([\\s\\S]*?)<\\/script>/;\n const scriptMatch = scriptRegex.exec(code);\n let hasScriptExtraction = false;\n const scriptContent = scriptMatch ? scriptMatch[1] : '';\n\n if (scriptMatch) {\n // Calculate offset: scriptMatch.index is start of <script...\n // We need to find the end of the opening tag >\n const openTagEndIndex = scriptMatch[0].indexOf('>') + 1;\n const offset = scriptMatch.index + openTagEndIndex;\n\n try {\n const ast = parse(scriptContent, {\n parserOpts: {\n sourceType: 'module',\n plugins: ['typescript', 'jsx'],\n },\n });\n\n traverse(ast, {\n StringLiteral(path) {\n if (path.parentPath.isImportDeclaration()) return;\n if (path.parentPath.isExportDeclaration()) return;\n if (path.parentPath.isImportSpecifier()) return;\n if (path.parentPath.isObjectProperty() && path.key === 'key') return;\n\n if (path.parentPath.isCallExpression()) {\n const callee = path.parentPath.node.callee;\n if (\n t.isMemberExpression(callee) &&\n t.isIdentifier(callee.object) &&\n callee.object.name === 'console'\n ) {\n return;\n }\n if (\n t.isIdentifier(callee) &&\n (callee.name === 'useIntlayer' || callee.name === 't')\n ) {\n return;\n }\n\n // Check for dynamic import import()\n if (callee.type === 'Import') return;\n\n // Check for require()\n if (t.isIdentifier(callee) && callee.name === 'require') return;\n }\n\n const text = path.node.value;\n if (shouldExtract(text)) {\n const key = generateKey(text, existingKeys);\n existingKeys.add(key);\n extractedContent[key] = text.trim();\n hasScriptExtraction = true;\n\n if (path.node.start != null && path.node.end != null) {\n magic.overwrite(\n offset + path.node.start,\n offset + path.node.end,\n `get(content).${key}`\n );\n }\n }\n },\n });\n } catch (e) {\n console.warn(\n `Svelte extraction: Failed to parse script content for ${filename}`,\n e\n );\n }\n }\n\n // If nothing was extracted, return null\n if (Object.keys(extractedContent).length === 0) {\n return null;\n }\n\n // Check if useIntlayer is already imported\n const hasUseIntlayerImport =\n /import\\s*{[^}]*useIntlayer[^}]*}\\s*from\\s*['\"][^'\"]+['\"]/.test(\n scriptContent\n ) || /import\\s+useIntlayer\\s+from\\s*['\"][^'\"]+['\"]/.test(scriptContent);\n\n // Check if get is already imported from svelte/store\n const hasGetImport =\n /import\\s*{[^}]*get[^}]*}\\s*from\\s*['\"]svelte\\/store['\"]/.test(\n scriptContent\n );\n\n // Check if content variable is already declared with useIntlayer\n const hasContentDeclaration = /const\\s+content\\s*=\\s*useIntlayer\\s*\\(/.test(\n scriptContent\n );\n\n // Skip injection if already using useIntlayer\n if (hasUseIntlayerImport && hasContentDeclaration) {\n return null;\n }\n\n // Prepare injection statements (only what's missing)\n const importStmt = hasUseIntlayerImport\n ? ''\n : `import { useIntlayer } from '${packageName}';`;\n\n const getImportStmt =\n hasScriptExtraction && !hasGetImport\n ? `import { get } from 'svelte/store';`\n : '';\n\n const contentDecl = hasContentDeclaration\n ? ''\n : `const content = useIntlayer('${dictionaryKey}');`;\n\n // Build injection string\n const injectionParts = [importStmt, getImportStmt, contentDecl].filter(\n Boolean\n );\n if (injectionParts.length === 0) {\n return null;\n }\n const injection = `\\n ${injectionParts.join('\\n ')}\\n`;\n\n if (scriptMatch) {\n // Insert at the beginning of script content\n const scriptContentStart =\n scriptMatch.index + scriptMatch[0].indexOf('>') + 1;\n magic.appendLeft(scriptContentStart, injection);\n } else {\n // No script block, create one\n magic.prepend(\n `<script>\\n ${importStmt}\\n ${hasScriptExtraction ? \"import { get } from 'svelte/store';\" : ''}\\n ${contentDecl}\\n</script>\\n\\n`\n );\n }\n\n // Call the onExtract callback with extracted content\n if (onExtract) {\n const result: ExtractResult = {\n dictionaryKey,\n filePath: filename,\n content: { ...extractedContent },\n locale: defaultLocale,\n };\n onExtract(result);\n }\n\n return {\n code: magic.toString(),\n map: magic.generateMap({ source: filename, includeContent: true }),\n extracted: true,\n };\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"svelte-intlayer-extract.mjs","names":["t"],"sources":["../../src/svelte-intlayer-extract.ts"],"sourcesContent":["import { basename, dirname, extname } from 'node:path';\nimport { parse, types as t, traverse } from '@babel/core';\nimport { generateKey } from '@intlayer/core/utils';\n\n/* ────────────────────────────────────────── constants ───────────────────── */\n\n/**\n * Attributes that should be extracted for localization\n */\nexport const ATTRIBUTES_TO_EXTRACT = [\n 'title',\n 'placeholder',\n 'alt',\n 'aria-label',\n 'label',\n];\n\n/* ────────────────────────────────────────── types ───────────────────────── */\n\nexport type ExtractedContent = Record<string, string>;\n\n/**\n * Extracted content result from a file transformation\n */\nexport type ExtractResult = {\n /** Dictionary key derived from the file path */\n dictionaryKey: string;\n /** File path that was processed */\n filePath: string;\n /** Extracted content key-value pairs */\n content: ExtractedContent;\n /** Default locale used */\n locale: string;\n};\n\n/**\n * Options for extraction plugins\n */\nexport type ExtractPluginOptions = {\n /**\n * The default locale for the extracted content\n * @default 'en'\n */\n defaultLocale?: string;\n /**\n * The package to import useIntlayer from\n * @default 'svelte-intlayer'\n */\n packageName?: string;\n /**\n * Files list to traverse. If provided, only files in this list will be processed.\n */\n filesList?: string[];\n /**\n * Custom function to determine if a string should be extracted\n */\n shouldExtract?: (text: string) => boolean;\n /**\n * Callback function called when content is extracted from a file.\n * This allows the compiler to capture the extracted content and write it to files.\n * The dictionary will be updated: new keys added, unused keys removed.\n */\n onExtract?: (result: ExtractResult) => void;\n};\n\n/* ────────────────────────────────────────── helpers ─────────────────────── */\n\n/**\n * Default function to determine if a string should be extracted\n */\nexport const defaultShouldExtract = (text: string): boolean => {\n const trimmed = text.trim();\n if (!trimmed) return false;\n // Must contain at least one space (likely a sentence/phrase)\n if (!trimmed.includes(' ')) return false;\n // Must start with a capital letter\n if (!/^[A-Z]/.test(trimmed)) return false;\n // Filter out template logic identifiers\n if (trimmed.startsWith('{') || trimmed.startsWith('v-')) return false;\n return true;\n};\n\n/**\n * Extract dictionary key from file path\n */\nexport const extractDictionaryKeyFromPath = (filePath: string): string => {\n const ext = extname(filePath);\n let baseName = basename(filePath, ext);\n\n if (baseName === 'index') {\n baseName = basename(dirname(filePath));\n }\n\n // Convert to kebab-case\n const key = baseName\n .replace(/([a-z])([A-Z])/g, '$1-$2')\n .replace(/[\\s_]+/g, '-')\n .toLowerCase();\n\n return `comp-${key}`;\n};\n\n/**\n * Check if a file should be processed based on filesList\n */\nexport const shouldProcessFile = (\n filename: string | undefined,\n filesList?: string[]\n): boolean => {\n if (!filename) return false;\n if (!filesList || filesList.length === 0) return true;\n\n // Normalize paths for comparison (handle potential path separator issues)\n const normalizedFilename = filename.replace(/\\\\/g, '/');\n return filesList.some((f) => {\n const normalizedF = f.replace(/\\\\/g, '/');\n return normalizedF === normalizedFilename;\n });\n};\n\n/* ────────────────────────────────────────── MagicString type ────────────── */\n\n// MagicString type for dynamic import\ntype MagicStringType = {\n overwrite: (start: number, end: number, content: string) => void;\n appendLeft: (index: number, content: string) => void;\n prepend: (content: string) => void;\n toString: () => string;\n generateMap: (options: {\n source: string;\n includeContent: boolean;\n }) => unknown;\n};\n\n/* ────────────────────────────────────────── plugin ──────────────────────── */\n\n/**\n * Svelte extraction plugin that extracts content and transforms Svelte components to use useIntlayer.\n *\n * This plugin:\n * 1. Scans Svelte files for extractable text (template text, attributes)\n * 2. Auto-injects useIntlayer import and store binding\n * 3. Reports extracted content via onExtract callback (for the compiler to write dictionaries)\n * 4. Replaces extractable strings with content references using Svelte's reactive `$` prefix\n *\n * ## Input\n * ```svelte\n * <h1>Hello World</h1>\n * <p>Welcome to our app</p>\n * ```\n *\n * ## Output\n * ```svelte\n * <script>\n * import { useIntlayer } from 'svelte-intlayer';\n * const content = useIntlayer('hello-world');\n * </script>\n * <h1>{$content.helloWorld}</h1>\n * <p>{$content.welcomeToOurApp}</p>\n * ```\n *\n * Note: Svelte uses reactive stores with `$` prefix for automatic subscription.\n * The `useIntlayer` composable returns a Svelte store that can be accessed reactively.\n */\nexport const intlayerSvelteExtract = async (\n code: string,\n filename: string,\n options: ExtractPluginOptions = {}\n): Promise<{ code: string; map?: unknown; extracted: boolean } | null> => {\n const {\n defaultLocale = 'en',\n packageName = 'svelte-intlayer',\n filesList,\n shouldExtract = defaultShouldExtract,\n onExtract,\n } = options;\n\n // Check if file should be processed\n if (!shouldProcessFile(filename, filesList)) {\n return null;\n }\n\n // Skip non-Svelte files\n if (!filename.endsWith('.svelte')) {\n return null;\n }\n\n // Dynamic import for MagicString\n let MagicString: new (code: string) => MagicStringType;\n\n try {\n const magicStringModule = await import('magic-string');\n MagicString = magicStringModule.default;\n } catch {\n console.warn(\n 'Svelte extraction: magic-string not found. Install it to enable Svelte content extraction.'\n );\n return null;\n }\n\n const magic = new MagicString(code);\n const extractedContent: ExtractedContent = {};\n const existingKeys = new Set<string>();\n const dictionaryKey = extractDictionaryKeyFromPath(filename);\n\n // Collect all replacements first, then apply them in reverse order\n // This prevents MagicString \"chunk already edited\" errors\n type Replacement = {\n start: number;\n end: number;\n replacement: string;\n key: string;\n value: string;\n };\n const replacements: Replacement[] = [];\n\n // Extract template content (everything outside <script> and <style> tags)\n // This regex-based approach works with both TypeScript and JavaScript files\n const scriptBlockRegex = /<script[^>]*>[\\s\\S]*?<\\/script>/gi;\n const styleBlockRegex = /<style[^>]*>[\\s\\S]*?<\\/style>/gi;\n\n // Get ranges of script and style blocks to skip\n const skipRanges: Array<{ start: number; end: number }> = [];\n\n // Find all script blocks\n const scriptMatches = code.matchAll(scriptBlockRegex);\n for (const match of scriptMatches) {\n if (match.index !== undefined) {\n skipRanges.push({\n start: match.index,\n end: match.index + match[0].length,\n });\n }\n }\n\n // Find all style blocks\n const styleMatches = code.matchAll(styleBlockRegex);\n for (const match of styleMatches) {\n if (match.index !== undefined) {\n skipRanges.push({\n start: match.index,\n end: match.index + match[0].length,\n });\n }\n }\n\n // Sort ranges by start position\n skipRanges.sort((a, b) => a.start - b.start);\n\n // Function to check if a position is within a skip range\n const isInSkipRange = (pos: number): boolean => {\n return skipRanges.some((range) => pos >= range.start && pos < range.end);\n };\n\n // Extract text content between HTML tags (but not inside script/style)\n // Match text that's between > and < (tag content)\n const textContentRegex = />([^<]+)</g;\n const textMatches = code.matchAll(textContentRegex);\n for (const match of textMatches) {\n if (match.index === undefined) continue;\n\n const textStart = match.index + 1; // Skip the >\n const text = match[1];\n const textEnd = textStart + text.length;\n\n // Skip if inside script or style block\n if (isInSkipRange(textStart)) {\n continue;\n }\n\n if (shouldExtract(text)) {\n const key = generateKey(text, existingKeys);\n existingKeys.add(key);\n const normalizedValue = text.replace(/\\s+/g, ' ').trim();\n // Collect replacement instead of applying immediately\n replacements.push({\n start: textStart,\n end: textEnd,\n replacement: `{$content.${key}}`,\n key,\n value: normalizedValue,\n });\n }\n }\n\n // Extract localizable attributes (title, placeholder, alt, aria-label, label)\n for (const attrName of ATTRIBUTES_TO_EXTRACT) {\n // Match attribute=\"value\" or attribute='value'\n const attrRegex = new RegExp(`(${attrName})=[\"']([^\"']+)[\"']`, 'gi');\n const attrMatches = code.matchAll(attrRegex);\n for (const match of attrMatches) {\n if (match.index === undefined) continue;\n\n const attrStart = match.index;\n const attrEnd = attrStart + match[0].length;\n const text = match[2];\n\n // Skip if inside script or style block\n if (isInSkipRange(attrStart)) {\n continue;\n }\n\n if (shouldExtract(text)) {\n const key = generateKey(text, existingKeys);\n existingKeys.add(key);\n // Collect replacement instead of applying immediately\n replacements.push({\n start: attrStart,\n end: attrEnd,\n replacement: `${attrName}={$content.${key}.value}`,\n key,\n value: text.trim(),\n });\n }\n }\n }\n\n // Sort replacements by start position in REVERSE order (end to start)\n // This ensures earlier edits don't affect the positions of later edits\n replacements.sort((a, b) => b.start - a.start);\n\n // Apply all replacements and collect extracted content\n for (const { start, end, replacement, key, value } of replacements) {\n magic.overwrite(start, end, replacement);\n extractedContent[key] = value;\n }\n\n // Extract script content\n const scriptRegex = /<script[^>]*>([\\s\\S]*?)<\\/script>/;\n const scriptMatch = scriptRegex.exec(code);\n let hasScriptExtraction = false;\n const scriptContent = scriptMatch ? scriptMatch[1] : '';\n\n if (scriptMatch) {\n // Calculate offset: scriptMatch.index is start of <script...\n // We need to find the end of the opening tag >\n const openTagEndIndex = scriptMatch[0].indexOf('>') + 1;\n const offset = scriptMatch.index + openTagEndIndex;\n\n try {\n const ast = parse(scriptContent, {\n parserOpts: {\n sourceType: 'module',\n plugins: ['typescript', 'jsx'],\n },\n });\n\n traverse(ast, {\n StringLiteral(path) {\n if (path.parentPath.isImportDeclaration()) return;\n if (path.parentPath.isExportDeclaration()) return;\n if (path.parentPath.isImportSpecifier()) return;\n if (path.parentPath.isObjectProperty() && path.key === 'key') return;\n\n if (path.parentPath.isCallExpression()) {\n const callee = path.parentPath.node.callee;\n if (\n t.isMemberExpression(callee) &&\n t.isIdentifier(callee.object) &&\n callee.object.name === 'console'\n ) {\n return;\n }\n if (\n t.isIdentifier(callee) &&\n (callee.name === 'useIntlayer' || callee.name === 't')\n ) {\n return;\n }\n\n // Check for dynamic import import()\n if (callee.type === 'Import') return;\n\n // Check for require()\n if (t.isIdentifier(callee) && callee.name === 'require') return;\n }\n\n const text = path.node.value;\n if (shouldExtract(text)) {\n const key = generateKey(text, existingKeys);\n existingKeys.add(key);\n extractedContent[key] = text.trim();\n hasScriptExtraction = true;\n\n if (path.node.start != null && path.node.end != null) {\n magic.overwrite(\n offset + path.node.start,\n offset + path.node.end,\n `get(content).${key}`\n );\n }\n }\n },\n });\n } catch (e) {\n console.warn(\n `Svelte extraction: Failed to parse script content for ${filename}`,\n e\n );\n }\n }\n\n // If nothing was extracted, return null\n if (Object.keys(extractedContent).length === 0) {\n return null;\n }\n\n // Check if useIntlayer is already imported\n const hasUseIntlayerImport =\n /import\\s*{[^}]*useIntlayer[^}]*}\\s*from\\s*['\"][^'\"]+['\"]/.test(\n scriptContent\n ) || /import\\s+useIntlayer\\s+from\\s*['\"][^'\"]+['\"]/.test(scriptContent);\n\n // Check if get is already imported from svelte/store\n const hasGetImport =\n /import\\s*{[^}]*get[^}]*}\\s*from\\s*['\"]svelte\\/store['\"]/.test(\n scriptContent\n );\n\n // Check if content variable is already declared with useIntlayer\n const hasContentDeclaration = /const\\s+content\\s*=\\s*useIntlayer\\s*\\(/.test(\n scriptContent\n );\n\n // Skip injection if already using useIntlayer\n if (hasUseIntlayerImport && hasContentDeclaration) {\n return null;\n }\n\n // Prepare injection statements (only what's missing)\n const importStmt = hasUseIntlayerImport\n ? ''\n : `import { useIntlayer } from '${packageName}';`;\n\n const getImportStmt =\n hasScriptExtraction && !hasGetImport\n ? `import { get } from 'svelte/store';`\n : '';\n\n const contentDecl = hasContentDeclaration\n ? ''\n : `const content = useIntlayer('${dictionaryKey}');`;\n\n // Build injection string\n const injectionParts = [importStmt, getImportStmt, contentDecl].filter(\n Boolean\n );\n if (injectionParts.length === 0) {\n return null;\n }\n const injection = `\\n ${injectionParts.join('\\n ')}\\n`;\n\n if (scriptMatch) {\n // Insert at the beginning of script content\n const scriptContentStart =\n scriptMatch.index + scriptMatch[0].indexOf('>') + 1;\n magic.appendLeft(scriptContentStart, injection);\n } else {\n // No script block, create one\n magic.prepend(\n `<script>\\n ${importStmt}\\n ${hasScriptExtraction ? \"import { get } from 'svelte/store';\" : ''}\\n ${contentDecl}\\n</script>\\n\\n`\n );\n }\n\n // Call the onExtract callback with extracted content\n if (onExtract) {\n const result: ExtractResult = {\n dictionaryKey,\n filePath: filename,\n content: { ...extractedContent },\n locale: defaultLocale,\n };\n onExtract(result);\n }\n\n return {\n code: magic.toString(),\n map: magic.generateMap({ source: filename, includeContent: true }),\n extracted: true,\n };\n};\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
1
  import { CompilerMode, HotUpdateContext, SvelteIntlayerCompiler, SvelteIntlayerCompilerOptions, SvelteIntlayerVitePlugin, TransformResult, createSvelteIntlayerCompiler, svelteIntlayerCompiler } from "./SvelteIntlayerCompiler.js";
2
- import { ATTRIBUTES_TO_EXTRACT, ExtractPluginOptions, ExtractResult, ExtractedContent, defaultShouldExtract, extractDictionaryKeyFromPath, generateKey, intlayerSvelteExtract, shouldProcessFile } from "./svelte-intlayer-extract.js";
3
- export { ATTRIBUTES_TO_EXTRACT, type CompilerMode, type ExtractPluginOptions, type ExtractResult, type ExtractedContent, type HotUpdateContext, SvelteIntlayerCompiler, type SvelteIntlayerCompilerOptions, type SvelteIntlayerVitePlugin, type TransformResult, createSvelteIntlayerCompiler, defaultShouldExtract, extractDictionaryKeyFromPath, generateKey, intlayerSvelteExtract, shouldProcessFile, svelteIntlayerCompiler };
2
+ import { ATTRIBUTES_TO_EXTRACT, ExtractPluginOptions, ExtractResult, ExtractedContent, defaultShouldExtract, extractDictionaryKeyFromPath, intlayerSvelteExtract, shouldProcessFile } from "./svelte-intlayer-extract.js";
3
+ export { ATTRIBUTES_TO_EXTRACT, type CompilerMode, type ExtractPluginOptions, type ExtractResult, type ExtractedContent, type HotUpdateContext, SvelteIntlayerCompiler, type SvelteIntlayerCompilerOptions, type SvelteIntlayerVitePlugin, type TransformResult, createSvelteIntlayerCompiler, defaultShouldExtract, extractDictionaryKeyFromPath, intlayerSvelteExtract, shouldProcessFile, svelteIntlayerCompiler };
@@ -46,10 +46,6 @@ type ExtractPluginOptions = {
46
46
  * Default function to determine if a string should be extracted
47
47
  */
48
48
  declare const defaultShouldExtract: (text: string) => boolean;
49
- /**
50
- * Generate a unique key from text
51
- */
52
- declare const generateKey: (text: string, existingKeys: Set<string>) => string;
53
49
  /**
54
50
  * Extract dictionary key from file path
55
51
  */
@@ -92,5 +88,5 @@ declare const intlayerSvelteExtract: (code: string, filename: string, options?:
92
88
  extracted: boolean;
93
89
  } | null>;
94
90
  //#endregion
95
- export { ATTRIBUTES_TO_EXTRACT, ExtractPluginOptions, ExtractResult, ExtractedContent, defaultShouldExtract, extractDictionaryKeyFromPath, generateKey, intlayerSvelteExtract, shouldProcessFile };
91
+ export { ATTRIBUTES_TO_EXTRACT, ExtractPluginOptions, ExtractResult, ExtractedContent, defaultShouldExtract, extractDictionaryKeyFromPath, intlayerSvelteExtract, shouldProcessFile };
96
92
  //# sourceMappingURL=svelte-intlayer-extract.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"svelte-intlayer-extract.d.ts","names":[],"sources":["../../src/svelte-intlayer-extract.ts"],"mappings":";;AAQA;;cAAa,qBAAA;AAAA,KAUD,gBAAA,GAAmB,MAAA;;AAA/B;;KAKY,aAAA;EALmB,gDAO7B,aAAA,UAFU;EAIV,QAAA;EAEA,OAAA,EAAS,gBAAA,EAJT;EAMA,MAAA;AAAA;;;;KAMU,oBAAA;EAAA;;;;EAKV,aAAA;EAKA;;;;EAAA,WAAA;EAcqB;;;EAVrB,SAAA;EAkBW;;;EAdX,aAAA,IAAiB,IAAA;EAc8B;AAejD;;;;EAvBE,SAAA,IAAa,MAAA,EAAQ,aAAA;AAAA;;;;cAQV,oBAAA,GAAwB,IAAA;;;;cAexB,WAAA,GACX,IAAA,UACA,YAAA,EAAc,GAAA;AAmDhB;;;AAAA,cApBa,4BAAA,GAAgC,QAAA;;AA+E7C;;cA3Da,iBAAA,GACX,QAAA,sBACA,SAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAyDW,qBAAA,GACX,IAAA,UACA,QAAA,UACA,OAAA,GAAS,oBAAA,KACR,OAAA;EAAU,IAAA;EAAc,GAAA;EAAe,SAAA;AAAA"}
1
+ {"version":3,"file":"svelte-intlayer-extract.d.ts","names":[],"sources":["../../src/svelte-intlayer-extract.ts"],"mappings":";;AASA;;cAAa,qBAAA;AAAA,KAUD,gBAAA,GAAmB,MAAA;;AAA/B;;KAKY,aAAA;EALmB,gDAO7B,aAAA,UAFU;EAIV,QAAA;EAEA,OAAA,EAAS,gBAAA,EAJT;EAMA,MAAA;AAAA;;;;KAMU,oBAAA;EAAA;;;;EAKV,aAAA;EAKA;;;;EAAA,WAAA;EAcqB;;;EAVrB,SAAA;EAkBW;;;EAdX,aAAA,IAAiB,IAAA;EAc8B;AAejD;;;;EAvBE,SAAA,IAAa,MAAA,EAAQ,aAAA;AAAA;;;;cAQV,oBAAA,GAAwB,IAAA;AA8FrC;;;AAAA,cA/Ea,4BAAA,GAAgC,QAAA;;;;cAoBhC,iBAAA,GACX,QAAA,sBACA,SAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAyDW,qBAAA,GACX,IAAA,UACA,QAAA,UACA,OAAA,GAAS,oBAAA,KACR,OAAA;EAAU,IAAA;EAAc,GAAA;EAAe,SAAA;AAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intlayer/svelte-compiler",
3
- "version": "8.1.4",
3
+ "version": "8.1.5",
4
4
  "private": false,
5
5
  "description": "Vite-compatible compiler plugin for Svelte with Intlayer, providing HMR support, file transformation, and optimized dictionary loading for Svelte applications.",
6
6
  "keywords": [
@@ -82,11 +82,12 @@
82
82
  },
83
83
  "dependencies": {
84
84
  "@babel/core": "7.28.6",
85
- "@intlayer/babel": "8.1.4",
86
- "@intlayer/chokidar": "8.1.4",
87
- "@intlayer/cli": "8.1.4",
85
+ "@intlayer/babel": "8.1.5",
86
+ "@intlayer/chokidar": "8.1.5",
87
+ "@intlayer/cli": "8.1.5",
88
88
  "@intlayer/config": "8.1.4",
89
- "@intlayer/dictionaries-entry": "8.1.4",
89
+ "@intlayer/core": "8.1.4",
90
+ "@intlayer/dictionaries-entry": "8.1.5",
90
91
  "@intlayer/types": "8.1.4",
91
92
  "fast-glob": "3.3.3",
92
93
  "magic-string": "0.30.17"