svelte-preprocess-cssmodules
Advanced tools
Comparing version
@@ -1,2 +0,2 @@ | ||
import type { PreprocessorGroup } from 'svelte/types/compiler/preprocess/index'; | ||
import type { PreprocessorGroup } from 'svelte/compiler'; | ||
import type { PluginOptions } from './types'; | ||
@@ -6,2 +6,1 @@ declare const _default: (options?: Partial<PluginOptions>) => PreprocessorGroup; | ||
export declare const cssModules: (options?: Partial<PluginOptions>) => PreprocessorGroup; | ||
export declare const linearPreprocess: (preprocessors: PreprocessorGroup[]) => PreprocessorGroup[]; |
@@ -12,3 +12,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.linearPreprocess = exports.cssModules = void 0; | ||
exports.cssModules = void 0; | ||
const compiler_1 = require("svelte/compiler"); | ||
@@ -41,6 +41,6 @@ const processors_1 = require("./processors"); | ||
try { | ||
ast = (0, compiler_1.parse)(content, { filename }); | ||
ast = (0, compiler_1.parse)(content, { modern: true, filename }); | ||
} | ||
catch (err) { | ||
throw new Error(`${err}\n\nThe svelte component failed to be parsed. Make sure cssModules is running after all other preprocessors by wrapping them with "linearPreprocess()" from svelte-preprocess-cssmodules`); | ||
throw new Error(`${err}\n\nThe svelte component failed to be parsed.`); | ||
} | ||
@@ -65,20 +65,11 @@ if (!pluginOptions.useAsDefaultScoping && | ||
}); | ||
let parsedContent; | ||
switch (mode) { | ||
case 'scoped': { | ||
parsedContent = yield (0, processors_1.scopedProcessor)(ast, content, filename, pluginOptions); | ||
break; | ||
} | ||
case 'mixed': { | ||
parsedContent = yield (0, processors_1.mixedProcessor)(ast, content, filename, pluginOptions); | ||
break; | ||
} | ||
default: { | ||
parsedContent = yield (0, processors_1.nativeProcessor)(ast, content, filename, pluginOptions); | ||
break; | ||
} | ||
let processor = processors_1.nativeProcessor; | ||
if (mode === 'mixed') { | ||
processor = processors_1.mixedProcessor; | ||
} | ||
return { | ||
code: parsedContent, | ||
}; | ||
else if (mode === 'scoped') { | ||
processor = processors_1.scopedProcessor; | ||
} | ||
const parsedContent = yield processor(ast, content, filename, pluginOptions); | ||
return { code: parsedContent }; | ||
}); | ||
@@ -94,17 +85,3 @@ const cssModulesPreprocessor = (options = {}) => { | ||
}; | ||
const linearPreprocessor = (preprocessors) => { | ||
return preprocessors.map((p) => { | ||
return !p.script && !p.style | ||
? p | ||
: { | ||
markup({ content, filename }) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
return (0, compiler_1.preprocess)(content, p, { filename }); | ||
}); | ||
}, | ||
}; | ||
}); | ||
}; | ||
exports.default = exports = module.exports = cssModulesPreprocessor; | ||
exports.cssModules = cssModulesPreprocessor; | ||
exports.linearPreprocess = linearPreprocessor; |
@@ -1,5 +0,5 @@ | ||
import type { Ast } from 'svelte/types/compiler/interfaces.d'; | ||
import type { AST } from 'svelte/compiler'; | ||
export declare const normalizeIncludePaths: (paths: string[]) => string[]; | ||
export declare const isFileIncluded: (includePaths: string[], filename: string) => boolean; | ||
export declare const hasModuleImports: (content: string) => boolean; | ||
export declare const hasModuleAttribute: (ast: Ast) => boolean; | ||
export declare const hasModuleAttribute: (ast: AST.Root) => boolean; |
@@ -35,62 +35,70 @@ "use strict"; | ||
exports.default = (processor) => { | ||
const ast = processor.ast; | ||
const backup = { | ||
ast: processor.ast, | ||
magicContent: processor.magicContent, | ||
}; | ||
if (!processor.ast.instance) { | ||
return; | ||
} | ||
const backup = Object.assign({}, processor); | ||
let importedContent = ''; | ||
(0, estree_walker_1.walk)(ast, { | ||
(0, estree_walker_1.walk)(processor.ast.instance, { | ||
enter(baseNode) { | ||
const node = baseNode; | ||
if (node.type === 'Style' || node.type === 'Fragment') { | ||
this.skip(); | ||
} | ||
if (node.type === 'ImportDeclaration' && | ||
node.source.value.search(/\.module\.s?css$/) !== -1) { | ||
const absolutePath = path_1.default.resolve(path_1.default.dirname(processor.filename), node.source.value); | ||
const nodeModulesPath = path_1.default.resolve(`${path_1.default.resolve()}/node_modules`, node.source.value); | ||
try { | ||
processor.importedCssModuleList = {}; | ||
const fileContent = fs_1.default.readFileSync(absolutePath, 'utf8'); | ||
const fileStyle = `${processor.style.openTag}${fileContent}${processor.style.closeTag}`; | ||
let fileMagicContent = new magic_string_1.default(fileStyle); | ||
processor.ast = (0, compiler_1.parse)(fileStyle, { filename: absolutePath }); | ||
processor.magicContent = fileMagicContent; | ||
processor.styleParser(processor); | ||
fileMagicContent = processor.magicContent; | ||
processor.ast = backup.ast; | ||
processor.magicContent = backup.magicContent; | ||
if (node.specifiers.length === 0) { | ||
processor.magicContent.remove(node.start, node.end); | ||
var _a; | ||
(_a = baseNode.content) === null || _a === void 0 ? void 0 : _a.body.forEach((node) => { | ||
var _a; | ||
if (node.type === 'ImportDeclaration' && | ||
((_a = String(node.source.value)) === null || _a === void 0 ? void 0 : _a.search(/\.module\.s?css$/)) !== -1) { | ||
const nodeBody = node; | ||
const sourceValue = String(nodeBody.source.value); | ||
const absolutePath = path_1.default.resolve(path_1.default.dirname(processor.filename), sourceValue); | ||
const nodeModulesPath = path_1.default.resolve(`${path_1.default.resolve()}/node_modules`, sourceValue); | ||
try { | ||
processor.importedCssModuleList = {}; | ||
const fileContent = fs_1.default.readFileSync(absolutePath, 'utf8'); | ||
const fileStyle = `${processor.style.openTag}${fileContent}${processor.style.closeTag}`; | ||
let fileMagicContent = new magic_string_1.default(fileStyle); | ||
processor.ast = (0, compiler_1.parse)(fileStyle, { | ||
filename: absolutePath, | ||
modern: true, | ||
}); | ||
processor.magicContent = fileMagicContent; | ||
processor.cssKeyframeList = {}; | ||
processor.cssAnimationProperties = []; | ||
processor.styleParser(processor); | ||
fileMagicContent = processor.magicContent; | ||
processor.ast = backup.ast; | ||
processor.magicContent = backup.magicContent; | ||
processor.cssKeyframeList = backup.cssKeyframeList; | ||
processor.cssAnimationProperties = backup.cssAnimationProperties; | ||
if (nodeBody.specifiers.length === 0) { | ||
processor.magicContent.remove(nodeBody.start, nodeBody.end); | ||
} | ||
else if (nodeBody.specifiers[0].type === 'ImportDefaultSpecifier') { | ||
const specifiers = `const ${nodeBody.specifiers[0].local.name} = ${JSON.stringify(processor.importedCssModuleList)};`; | ||
processor.magicContent.overwrite(nodeBody.start, nodeBody.end, specifiers); | ||
} | ||
else { | ||
const specifierNames = nodeBody.specifiers.map((item) => { | ||
return item.local.name; | ||
}); | ||
const specifiers = `const { ${specifierNames.join(', ')} } = ${JSON.stringify(Object.fromEntries(Object.entries(processor.importedCssModuleList).filter(([key]) => specifierNames.includes(key))))};`; | ||
processor.magicContent.overwrite(nodeBody.start, nodeBody.end, specifiers); | ||
} | ||
const content = `\n${fileMagicContent | ||
.toString() | ||
.replace(processor.style.openTag, '') | ||
.replace(processor.style.closeTag, '')}`; | ||
if (processor.style.ast) { | ||
processor.magicContent.prependLeft(processor.style.ast.content.start, content); | ||
} | ||
else { | ||
importedContent += content; | ||
} | ||
} | ||
else if (node.specifiers[0].type === 'ImportDefaultSpecifier') { | ||
const specifiers = `const ${node.specifiers[0].local.name} = ${JSON.stringify(processor.importedCssModuleList)};`; | ||
processor.magicContent.overwrite(node.start, node.end, specifiers); | ||
} | ||
else { | ||
const specifierNames = node.specifiers.map((item) => { | ||
return item.local.name; | ||
catch (err) { | ||
fs_1.default.access(nodeModulesPath, fs_1.constants.F_OK, (error) => { | ||
if (error) { | ||
throw new Error(err); | ||
} | ||
}); | ||
const specifiers = `const { ${specifierNames.join(', ')} } = ${JSON.stringify(Object.fromEntries(Object.entries(processor.importedCssModuleList).filter(([key]) => specifierNames.includes(key))))};`; | ||
processor.magicContent.overwrite(node.start, node.end, specifiers); | ||
} | ||
const content = `\n${fileMagicContent | ||
.toString() | ||
.replace(processor.style.openTag, '') | ||
.replace(processor.style.closeTag, '')}`; | ||
if (processor.style.ast) { | ||
processor.magicContent.prependLeft(processor.style.ast.content.start, content); | ||
} | ||
else { | ||
importedContent += content; | ||
} | ||
} | ||
catch (err) { | ||
fs_1.default.access(nodeModulesPath, fs_1.constants.F_OK, (error) => { | ||
if (error) { | ||
throw new Error(err); | ||
} | ||
}); | ||
} | ||
} | ||
}); | ||
}, | ||
@@ -97,0 +105,0 @@ }); |
@@ -20,20 +20,28 @@ "use strict"; | ||
const parseExpression = (processor, expression) => { | ||
if (expression.type === 'Literal') { | ||
const generatedClassNames = updateMultipleClasses(processor, expression.value); | ||
processor.magicContent.overwrite(expression.start, expression.end, `'${generatedClassNames}'`); | ||
const exp = expression; | ||
if (exp.type === 'Literal' && typeof exp.value === 'string') { | ||
const generatedClassNames = updateMultipleClasses(processor, exp.value); | ||
processor.magicContent.overwrite(exp.start, exp.end, `'${generatedClassNames}'`); | ||
} | ||
}; | ||
const addDynamicVariablesToElements = (processor, node, cssVar) => { | ||
const addDynamicVariablesToElements = (processor, fragment, cssVar) => { | ||
var _a; | ||
(_a = node === null || node === void 0 ? void 0 : node.children) === null || _a === void 0 ? void 0 : _a.forEach((childNode) => { | ||
if (childNode.type === 'InlineComponent' || | ||
childNode.type === 'EachBlock' || | ||
childNode.type === 'KeyBlock') { | ||
addDynamicVariablesToElements(processor, childNode, cssVar); | ||
(_a = fragment.nodes) === null || _a === void 0 ? void 0 : _a.forEach((childNode) => { | ||
if (childNode.type === 'Component' || childNode.type === 'KeyBlock') { | ||
addDynamicVariablesToElements(processor, childNode.fragment, cssVar); | ||
} | ||
else if (childNode.type === 'Element') { | ||
else if (childNode.type === 'EachBlock') { | ||
addDynamicVariablesToElements(processor, childNode.body, cssVar); | ||
if (childNode.fallback) { | ||
addDynamicVariablesToElements(processor, childNode.fallback, cssVar); | ||
} | ||
} | ||
else if (childNode.type === 'SnippetBlock') { | ||
addDynamicVariablesToElements(processor, childNode.body, cssVar); | ||
} | ||
else if (childNode.type === 'RegularElement') { | ||
const attributesLength = childNode.attributes.length; | ||
if (attributesLength) { | ||
const styleAttr = childNode.attributes.find((attr) => attr.name === 'style'); | ||
if (styleAttr) { | ||
const styleAttr = childNode.attributes.find((attr) => attr.type !== 'SpreadAttribute' && attr.name === 'style'); | ||
if (styleAttr && Array.isArray(styleAttr.value)) { | ||
processor.magicContent.appendLeft(styleAttr.value[0].start, cssVar.values); | ||
@@ -51,9 +59,17 @@ } | ||
else if (childNode.type === 'IfBlock') { | ||
addDynamicVariablesToElements(processor, childNode, cssVar); | ||
addDynamicVariablesToElements(processor, childNode.else, cssVar); | ||
addDynamicVariablesToElements(processor, childNode.consequent, cssVar); | ||
if (childNode.alternate) { | ||
addDynamicVariablesToElements(processor, childNode.alternate, cssVar); | ||
} | ||
} | ||
else if (childNode.type === 'AwaitBlock') { | ||
addDynamicVariablesToElements(processor, childNode.pending, cssVar); | ||
addDynamicVariablesToElements(processor, childNode.then, cssVar); | ||
addDynamicVariablesToElements(processor, childNode.catch, cssVar); | ||
if (childNode.pending) { | ||
addDynamicVariablesToElements(processor, childNode.pending, cssVar); | ||
} | ||
if (childNode.then) { | ||
addDynamicVariablesToElements(processor, childNode.then, cssVar); | ||
} | ||
if (childNode.catch) { | ||
addDynamicVariablesToElements(processor, childNode.catch, cssVar); | ||
} | ||
} | ||
@@ -79,14 +95,16 @@ }); | ||
const cssVar = cssVariables(processor); | ||
(0, estree_walker_1.walk)(processor.ast.html, { | ||
let dynamicVariablesAdded = false; | ||
(0, estree_walker_1.walk)(processor.ast.fragment, { | ||
enter(baseNode) { | ||
const node = baseNode; | ||
if (node.type === 'Script' || node.type === 'Style') { | ||
this.skip(); | ||
} | ||
if (node.type === 'Fragment' && cssVar.values.length) { | ||
if (node.type === 'Fragment' && cssVar.values.length && !dynamicVariablesAdded) { | ||
dynamicVariablesAdded = true; | ||
addDynamicVariablesToElements(processor, node, cssVar); | ||
} | ||
if (['Element', 'InlineComponent'].includes(node.type) && node.attributes.length > 0) { | ||
if (['RegularElement', 'Component'].includes(node.type) && | ||
node.attributes.length > 0) { | ||
node.attributes.forEach((item) => { | ||
if (item.type === 'Attribute' && allowedAttributes.includes(item.name)) { | ||
if (item.type === 'Attribute' && | ||
allowedAttributes.includes(item.name) && | ||
Array.isArray(item.value)) { | ||
item.value.forEach((classItem) => { | ||
@@ -98,3 +116,3 @@ var _a; | ||
} | ||
else if (classItem.type === 'MustacheTag' && | ||
else if (classItem.type === 'ExpressionTag' && | ||
((_a = classItem === null || classItem === void 0 ? void 0 : classItem.expression) === null || _a === void 0 ? void 0 : _a.type) === 'ConditionalExpression') { | ||
@@ -107,3 +125,3 @@ const { consequent, alternate } = classItem.expression; | ||
} | ||
if (item.type === 'Class') { | ||
if (item.type === 'ClassDirective') { | ||
const classNames = item.name.split('.'); | ||
@@ -110,0 +128,0 @@ const name = classNames.length > 1 ? classNames[1] : classNames[0]; |
@@ -1,4 +0,4 @@ | ||
import type { Ast } from 'svelte/types/compiler/interfaces'; | ||
import type { AST } from 'svelte/compiler'; | ||
import type { PluginOptions } from '../types'; | ||
declare const mixedProcessor: (ast: Ast, content: string, filename: string, options: PluginOptions) => Promise<string>; | ||
declare const mixedProcessor: (ast: AST.Root, content: string, filename: string, options: PluginOptions) => Promise<string>; | ||
export default mixedProcessor; |
@@ -29,63 +29,66 @@ "use strict"; | ||
const parser = (processor) => { | ||
const ast = processor.ast; | ||
(0, estree_walker_1.walk)(ast, { | ||
if (!processor.ast.css) { | ||
return; | ||
} | ||
(0, estree_walker_1.walk)(processor.ast.css, { | ||
enter(baseNode) { | ||
const node = baseNode; | ||
if (node.type === 'Script' || node.type === 'Fragment') { | ||
this.skip(); | ||
} | ||
if (node.type === 'Atrule' && node.name === 'keyframes') { | ||
processor.parseKeyframes(node); | ||
this.skip(); | ||
} | ||
if (node.type === 'Selector') { | ||
const classSelectors = node.children | ||
? node.children.filter((item) => item.type === 'ClassSelector') | ||
: []; | ||
if (classSelectors.length > 0) { | ||
let selectorBoundaries = []; | ||
let start = 0; | ||
let end = 0; | ||
if (node.children) { | ||
node.children.forEach((item, index) => { | ||
if (!item.start && start > 0) { | ||
selectorBoundaries = updateSelectorBoundaries(selectorBoundaries, start, end); | ||
start = 0; | ||
end = 0; | ||
} | ||
else { | ||
let hasPushed = false; | ||
if (end !== item.start) { | ||
start = item.start; | ||
end = item.end; | ||
var _a; | ||
(_a = baseNode.children) === null || _a === void 0 ? void 0 : _a.forEach((node) => { | ||
if (node.type === 'Atrule' && node.name === 'keyframes') { | ||
processor.parseKeyframes(node); | ||
this.skip(); | ||
} | ||
if (node.type === 'Rule') { | ||
node.prelude.children.forEach((child) => { | ||
child.children.forEach((grandChild) => { | ||
if (grandChild.type === 'RelativeSelector') { | ||
const classSelectors = grandChild.selectors.filter((item) => item.type === 'ClassSelector'); | ||
if (classSelectors.length > 0) { | ||
let selectorBoundaries = []; | ||
let start = 0; | ||
let end = 0; | ||
grandChild.selectors.forEach((item, index) => { | ||
if (!item.start && start > 0) { | ||
selectorBoundaries = updateSelectorBoundaries(selectorBoundaries, start, end); | ||
start = 0; | ||
end = 0; | ||
} | ||
else { | ||
let hasPushed = false; | ||
if (end !== item.start) { | ||
start = item.start; | ||
end = item.end; | ||
} | ||
else { | ||
selectorBoundaries = updateSelectorBoundaries(selectorBoundaries, start, item.end); | ||
hasPushed = true; | ||
start = 0; | ||
end = 0; | ||
} | ||
if (hasPushed === false && | ||
grandChild.selectors && | ||
index === grandChild.selectors.length - 1) { | ||
selectorBoundaries = updateSelectorBoundaries(selectorBoundaries, start, end); | ||
} | ||
} | ||
}); | ||
selectorBoundaries.forEach((boundary) => { | ||
const hasClassSelector = classSelectors.filter((item) => boundary.start <= item.start && boundary.end >= item.end); | ||
if (hasClassSelector.length > 0) { | ||
processor.magicContent.appendLeft(boundary.start, ':global('); | ||
processor.magicContent.appendRight(boundary.end, ')'); | ||
} | ||
}); | ||
} | ||
else { | ||
selectorBoundaries = updateSelectorBoundaries(selectorBoundaries, start, item.end); | ||
hasPushed = true; | ||
start = 0; | ||
end = 0; | ||
} | ||
if (hasPushed === false && node.children && index === node.children.length - 1) { | ||
selectorBoundaries = updateSelectorBoundaries(selectorBoundaries, start, end); | ||
} | ||
grandChild.selectors.forEach((item) => { | ||
processor.parsePseudoLocalSelectors(item); | ||
processor.parseClassSelectors(item); | ||
}); | ||
} | ||
}); | ||
} | ||
selectorBoundaries.forEach((boundary) => { | ||
const hasClassSelector = classSelectors.filter((item) => boundary.start <= item.start && boundary.end >= item.end); | ||
if (hasClassSelector.length > 0) { | ||
processor.magicContent.appendLeft(boundary.start, ':global('); | ||
processor.magicContent.appendRight(boundary.end, ')'); | ||
} | ||
}); | ||
processor.parseBoundVariables(node.block); | ||
processor.storeAnimationProperties(node.block); | ||
} | ||
} | ||
processor.parseBoundVariables(node); | ||
processor.parsePseudoLocalSelectors(node); | ||
processor.storeAnimationProperties(node); | ||
if (node.type === 'ClassSelector') { | ||
const generatedClassName = processor.createModuleClassname(node.name); | ||
processor.addModule(node.name, generatedClassName); | ||
processor.magicContent.overwrite(node.start, node.end, `.${generatedClassName}`); | ||
} | ||
}); | ||
}, | ||
@@ -92,0 +95,0 @@ }); |
@@ -1,4 +0,4 @@ | ||
import type { Ast } from 'svelte/types/compiler/interfaces'; | ||
import type { AST } from 'svelte/compiler'; | ||
import type { PluginOptions } from '../types'; | ||
declare const nativeProcessor: (ast: Ast, content: string, filename: string, options: PluginOptions) => Promise<string>; | ||
declare const nativeProcessor: (ast: AST.Root, content: string, filename: string, options: PluginOptions) => Promise<string>; | ||
export default nativeProcessor; |
@@ -30,49 +30,55 @@ "use strict"; | ||
const parser = (processor) => { | ||
const ast = processor.ast; | ||
if (!processor.ast.css) { | ||
return; | ||
} | ||
let selectorBoundaries = []; | ||
(0, estree_walker_1.walk)(ast, { | ||
(0, estree_walker_1.walk)(processor.ast.css, { | ||
enter(baseNode) { | ||
const node = baseNode; | ||
if (node.type === 'Script' || node.type === 'Fragment') { | ||
this.skip(); | ||
} | ||
if (node.type === 'Atrule' && node.name === 'keyframes') { | ||
processor.parseKeyframes(node); | ||
this.skip(); | ||
} | ||
if (node.type === 'Selector') { | ||
let start = 0; | ||
let end = 0; | ||
if (node.children) { | ||
node.children.forEach((item, index) => { | ||
let hasPushed = false; | ||
if ((item.name === 'global' || item.name === 'local') && | ||
item.type === 'PseudoClassSelector') { | ||
if (start > 0 && end > 0) { | ||
selectorBoundaries = updateSelectorBoundaries(selectorBoundaries, start, end); | ||
hasPushed = true; | ||
} | ||
start = item.end + 1; | ||
end = 0; | ||
var _a; | ||
(_a = baseNode.children) === null || _a === void 0 ? void 0 : _a.forEach((node) => { | ||
if (node.type === 'Atrule' && node.name === 'keyframes') { | ||
processor.parseKeyframes(node); | ||
this.skip(); | ||
} | ||
if (node.type === 'Rule') { | ||
node.prelude.children.forEach((child) => { | ||
if (child.type === 'ComplexSelector') { | ||
let start = 0; | ||
let end = 0; | ||
child.children.forEach((grandChild, index) => { | ||
let hasPushed = false; | ||
if (grandChild.type === 'RelativeSelector') { | ||
grandChild.selectors.forEach((item) => { | ||
if (item.type === 'PseudoClassSelector' && | ||
(item.name === 'global' || item.name === 'local')) { | ||
processor.parsePseudoLocalSelectors(item); | ||
if (start > 0 && end > 0) { | ||
selectorBoundaries = updateSelectorBoundaries(selectorBoundaries, start, end); | ||
hasPushed = true; | ||
} | ||
start = item.end + 1; | ||
end = 0; | ||
} | ||
else if (item.start && item.end) { | ||
if (start === 0) { | ||
start = item.start; | ||
} | ||
end = item.end; | ||
processor.parseClassSelectors(item); | ||
} | ||
}); | ||
if (hasPushed === false && | ||
child.children && | ||
index === child.children.length - 1 && | ||
end > 0) { | ||
selectorBoundaries = updateSelectorBoundaries(selectorBoundaries, start, end); | ||
} | ||
} | ||
}); | ||
} | ||
else if (item.start && item.end) { | ||
if (start === 0) { | ||
start = item.start; | ||
} | ||
end = item.end; | ||
} | ||
if (!hasPushed && node.children && index === node.children.length - 1 && end > 0) { | ||
selectorBoundaries = updateSelectorBoundaries(selectorBoundaries, start, end); | ||
} | ||
}); | ||
processor.parseBoundVariables(node.block); | ||
processor.storeAnimationProperties(node.block); | ||
} | ||
} | ||
processor.parseBoundVariables(node); | ||
processor.parsePseudoLocalSelectors(node); | ||
processor.storeAnimationProperties(node); | ||
if (node.type === 'ClassSelector') { | ||
const generatedClassName = processor.createModuleClassname(node.name); | ||
processor.addModule(node.name, generatedClassName); | ||
processor.magicContent.overwrite(node.start, node.end, `.${generatedClassName}`); | ||
} | ||
}); | ||
}, | ||
@@ -79,0 +85,0 @@ }); |
import MagicString from 'magic-string'; | ||
import type { Ast, Style, TemplateNode } from 'svelte/types/compiler/interfaces.d'; | ||
import type { AST } from 'svelte/compiler'; | ||
import { CSSModuleList, PluginOptions } from '../types'; | ||
@@ -11,7 +11,7 @@ export default class Processor { | ||
cssKeyframeList: CSSModuleList; | ||
cssAnimationProperties: TemplateNode[]; | ||
cssAnimationProperties: AST.CSS.Declaration[]; | ||
importedCssModuleList: CSSModuleList; | ||
ast: Ast; | ||
ast: AST.Root; | ||
style: { | ||
ast?: Style; | ||
ast?: AST.Root['css']; | ||
openTag: string; | ||
@@ -23,11 +23,12 @@ closeTag: string; | ||
isParsingImports: boolean; | ||
constructor(ast: Ast, content: string, filename: string, options: PluginOptions, parser: (param: Processor) => void); | ||
constructor(ast: AST.Root, content: string, filename: string, options: PluginOptions, parser: (param: Processor) => void); | ||
createModuleClassname: (name: string) => string; | ||
addModule: (name: string, value: string) => void; | ||
parse: () => string; | ||
parseBoundVariables: (node: TemplateNode) => void; | ||
parseKeyframes: (node: TemplateNode) => void; | ||
parsePseudoLocalSelectors: (node: TemplateNode) => void; | ||
storeAnimationProperties: (node: TemplateNode) => void; | ||
parseBoundVariables: (node: AST.CSS.Block) => void; | ||
parseKeyframes: (node: AST.CSS.Atrule) => void; | ||
parseClassSelectors: (node: AST.CSS.SimpleSelector) => void; | ||
parsePseudoLocalSelectors: (node: AST.CSS.SimpleSelector) => void; | ||
storeAnimationProperties: (node: AST.CSS.Block) => void; | ||
overwriteAnimationProperties: () => void; | ||
} |
@@ -44,18 +44,16 @@ "use strict"; | ||
this.parseBoundVariables = (node) => { | ||
var _a, _b; | ||
const bindedVariableNodes = (_b = (_a = node.children) === null || _a === void 0 ? void 0 : _a.filter((item) => { var _a; return item.type === 'Function' && item.name === 'bind' && ((_a = node.children) === null || _a === void 0 ? void 0 : _a.length); })) !== null && _b !== void 0 ? _b : []; | ||
var _a; | ||
const bindedVariableNodes = ((_a = node.children.filter((item) => item.type === 'Declaration' && item.value.includes('bind('))) !== null && _a !== void 0 ? _a : []); | ||
if (bindedVariableNodes.length > 0) { | ||
bindedVariableNodes.forEach((item) => { | ||
var _a, _b, _c; | ||
if (item.children) { | ||
const child = item.children[0]; | ||
const name = (_a = child.name) !== null && _a !== void 0 ? _a : child.value.replace(/'|"/g, ''); | ||
const varName = child.type === 'String' ? name.replace(/\./, '-') : name; | ||
const generatedVarName = (0, lib_1.generateName)(this.filename, (_c = (_b = this.ast.css) === null || _b === void 0 ? void 0 : _b.content.styles) !== null && _c !== void 0 ? _c : '', varName, { | ||
hashSeeder: ['style', 'filepath'], | ||
localIdentName: `[local]-${this.options.cssVariableHash}`, | ||
}); | ||
this.magicContent.overwrite(item.start, item.end, `var(--${generatedVarName})`); | ||
this.cssVarList[name] = generatedVarName; | ||
} | ||
var _a, _b; | ||
const name = item.value.replace(/'|"|bind\(|\)/g, ''); | ||
const varName = name.replace(/\./, '-'); | ||
const generatedVarName = (0, lib_1.generateName)(this.filename, (_b = (_a = this.ast.css) === null || _a === void 0 ? void 0 : _a.content.styles) !== null && _b !== void 0 ? _b : '', varName, { | ||
hashSeeder: ['style', 'filepath'], | ||
localIdentName: `[local]-${this.options.cssVariableHash}`, | ||
}); | ||
const bindStart = item.end - item.value.length; | ||
this.magicContent.overwrite(bindStart, item.end, `var(--${generatedVarName})`); | ||
this.cssVarList[name] = generatedVarName; | ||
}); | ||
@@ -65,9 +63,18 @@ } | ||
this.parseKeyframes = (node) => { | ||
const rulePrelude = node.prelude.children[0]; | ||
if (rulePrelude.name.indexOf('-global-') === -1) { | ||
const animationName = this.createModuleClassname(rulePrelude.name); | ||
this.magicContent.overwrite(rulePrelude.start, rulePrelude.end, `-global-${animationName}`); | ||
this.cssKeyframeList[rulePrelude.name] = animationName; | ||
var _a; | ||
if (node.prelude.indexOf('-global-') === -1) { | ||
const animationName = this.createModuleClassname(node.prelude); | ||
if ((_a = node.block) === null || _a === void 0 ? void 0 : _a.end) { | ||
this.magicContent.overwrite(node.start, node.block.start - 1, `@keyframes -global-${animationName}`); | ||
this.cssKeyframeList[node.prelude] = animationName; | ||
} | ||
} | ||
}; | ||
this.parseClassSelectors = (node) => { | ||
if (node.type === 'ClassSelector') { | ||
const generatedClassName = this.createModuleClassname(node.name); | ||
this.addModule(node.name, generatedClassName); | ||
this.magicContent.overwrite(node.start, node.end, `.${generatedClassName}`); | ||
} | ||
}; | ||
this.parsePseudoLocalSelectors = (node) => { | ||
@@ -80,14 +87,6 @@ if (node.type === 'PseudoClassSelector' && node.name === 'local') { | ||
this.storeAnimationProperties = (node) => { | ||
if (node.type === 'Declaration' && ['animation', 'animation-name'].includes(node.property)) { | ||
let names = 0; | ||
let properties = 0; | ||
node.value.children.forEach((item) => { | ||
if (item.type === 'Identifier' && properties === names) { | ||
names += 1; | ||
this.cssAnimationProperties.push(item); | ||
} | ||
if (item.type === 'Operator' && item.value === ',') { | ||
properties += 1; | ||
} | ||
}); | ||
var _a; | ||
const animationNodes = ((_a = node.children.filter((item) => item.type === 'Declaration' && ['animation', 'animation-name'].includes(item.property))) !== null && _a !== void 0 ? _a : []); | ||
if (animationNodes.length > 0) { | ||
this.cssAnimationProperties.push(...animationNodes); | ||
} | ||
@@ -97,5 +96,10 @@ }; | ||
this.cssAnimationProperties.forEach((item) => { | ||
if (item.name in this.cssKeyframeList) { | ||
this.magicContent.overwrite(item.start, item.end, this.cssKeyframeList[item.name]); | ||
} | ||
Object.keys(this.cssKeyframeList).forEach((key) => { | ||
const index = item.value.indexOf(key); | ||
if (index > -1) { | ||
const keyStart = item.end - item.value.length + index; | ||
const keyEnd = keyStart + key.length; | ||
this.magicContent.overwrite(keyStart, keyEnd, this.cssKeyframeList[key]); | ||
} | ||
}); | ||
}); | ||
@@ -102,0 +106,0 @@ }; |
@@ -1,4 +0,4 @@ | ||
import type { Ast } from 'svelte/types/compiler/interfaces'; | ||
import type { AST } from 'svelte/compiler'; | ||
import type { PluginOptions } from '../types'; | ||
declare const scopedProcessor: (ast: Ast, content: string, filename: string, options: PluginOptions) => Promise<string>; | ||
declare const scopedProcessor: (ast: AST.Root, content: string, filename: string, options: PluginOptions) => Promise<string>; | ||
export default scopedProcessor; |
@@ -18,15 +18,23 @@ "use strict"; | ||
const parser = (processor) => { | ||
const ast = processor.ast; | ||
(0, estree_walker_1.walk)(ast, { | ||
if (!processor.ast.css) { | ||
return; | ||
} | ||
(0, estree_walker_1.walk)(processor.ast.css, { | ||
enter(baseNode) { | ||
const node = baseNode; | ||
if (node.type === 'Script' || node.type === 'Fragment') { | ||
this.skip(); | ||
} | ||
processor.parseBoundVariables(node); | ||
if (node.type === 'ClassSelector') { | ||
const generatedClassName = processor.createModuleClassname(node.name); | ||
processor.addModule(node.name, generatedClassName); | ||
processor.magicContent.overwrite(node.start, node.end, `.${generatedClassName}`); | ||
} | ||
var _a; | ||
(_a = baseNode.children) === null || _a === void 0 ? void 0 : _a.forEach((node) => { | ||
if (node.type === 'Rule') { | ||
node.prelude.children.forEach((child) => { | ||
child.children.forEach((grandChild) => { | ||
if (grandChild.type === 'RelativeSelector') { | ||
grandChild.selectors.forEach((item) => { | ||
processor.parsePseudoLocalSelectors(item); | ||
processor.parseClassSelectors(item); | ||
}); | ||
} | ||
}); | ||
}); | ||
processor.parseBoundVariables(node.block); | ||
} | ||
}); | ||
}, | ||
@@ -33,0 +41,0 @@ }); |
{ | ||
"name": "svelte-preprocess-cssmodules", | ||
"version": "2.2.5", | ||
"version": "3.0.0", | ||
"description": "Svelte preprocessor to generate CSS Modules classname on Svelte components", | ||
@@ -75,7 +75,7 @@ "keywords": [ | ||
"prettier": "^3.3.3", | ||
"svelte": "^3.59.2", | ||
"svelte": "^5.15.0", | ||
"typescript": "^4.9.5" | ||
}, | ||
"peerDependencies": { | ||
"svelte": "^3.20.0 || ^4.0.0" | ||
"svelte": "^5.15.0" | ||
}, | ||
@@ -82,0 +82,0 @@ "files": [ |
@@ -9,2 +9,4 @@ # Svelte preprocess CSS Modules | ||
for `svelte 4` and below, use version 2 of the preprocessor. | ||
## Table of Content | ||
@@ -34,4 +36,4 @@ | ||
- [Svelte Preprocess](#svelte-preprocess) | ||
- [Vite](#vite) | ||
- [Options](#options) | ||
- [Migrating from v1](#migrating-from-v1) | ||
- [Code example](#code-example) | ||
@@ -118,4 +120,4 @@ | ||
<script> | ||
let route = 'home'; | ||
$: isActive = route === 'home'; | ||
let route = $state('home'); | ||
let isActive = $derived(route === 'home'); | ||
</script> | ||
@@ -146,4 +148,4 @@ | ||
<script> | ||
let route = 'home'; | ||
$: active = route === 'home'; | ||
let route = $state('home'); | ||
let active = $derived(route === 'home'); | ||
</script> | ||
@@ -271,3 +273,3 @@ | ||
<script> | ||
let color = 'red'; | ||
let color = $state('red'); | ||
</script> | ||
@@ -310,5 +312,5 @@ | ||
<script> | ||
const style = { | ||
const style = $state({ | ||
opacity: 0; | ||
}; | ||
}); | ||
</script> | ||
@@ -336,5 +338,5 @@ | ||
<script> | ||
const style = { | ||
const style = $state({ | ||
opacity: 0; | ||
}; | ||
}); | ||
</script> | ||
@@ -365,4 +367,3 @@ | ||
<script> | ||
let className; | ||
export { className as class }; | ||
let { class: className } = $props(); | ||
</script> | ||
@@ -590,4 +591,4 @@ | ||
let isSuccess = true; | ||
$: notice = isSuccess ? success : error; | ||
let isSuccess = $state(true); | ||
let notice = $derived(isSuccess ? success : error); | ||
</script> | ||
@@ -825,6 +826,4 @@ | ||
Svelte is running the preprocessors by phases, going through all *markup* first, followed by *script* and then *style*. | ||
The CSS Modules preprocessor requires the compoment to be a standard svelte component (using vanilla js and vanilla css). if any other code, such as Typescript or Sass, is encountered, an error will be thrown. Therefore CSS Modules needs to be run at the very end. | ||
The CSS Modules preprocessor is doing all its work on the markup phase via `svelte.parse()` which requires the compoment to be a valid standard svelte component (using vanilla js and vanilla css). if any other code (such as typescript or sass) is encountered, an error will be thrown. | ||
```js | ||
@@ -835,7 +834,7 @@ import { typescript, scss } from 'svelte-preprocess'; | ||
... | ||
// svelte config: NOT working! | ||
// svelte config: | ||
preprocess: [ | ||
typescript(), // 2 run second on script phase | ||
scss(), // 3 run last on style phase | ||
cssModules(), // 1 run first on markup phase | ||
typescript(), | ||
scss(), | ||
cssModules(), // run last | ||
], | ||
@@ -845,18 +844,19 @@ ... | ||
As it is extremely common for developers to use `svelte-preprocess` in their application, CSS Modules provides a small utility to easily be incorporated with. `linearPreprocess` will ensure a linear process with the list of preprocessors. | ||
### Vite | ||
Set the `svelte.config.js` accordingly. | ||
```js | ||
import { typescript, scss } from 'svelte-preprocess'; | ||
import { cssModules, linearPreprocess } from 'svelte-preprocess-cssmodules'; | ||
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; | ||
import { cssModules } from 'svelte-preprocess-cssmodules'; | ||
... | ||
// svelte config: OK, processing one after another! | ||
preprocess: linearPreprocess([ | ||
typescript(), // 1 run first | ||
scss(), // 2 run second | ||
cssModules(), // 3 run last | ||
]), | ||
... | ||
export default { | ||
preprocess: [ | ||
vitePreprocess(), | ||
cssModules() | ||
] | ||
}; | ||
``` | ||
### Options | ||
@@ -1077,24 +1077,2 @@ Pass an object of the following properties | ||
## Migrating from v1 | ||
If you want to migrate an existing project to `v2` keeping the approach of the 1st version, follow the steps below: | ||
- Set the `mixed` mode from the global settings. | ||
```js | ||
// Preprocess config | ||
... | ||
preprocess: [ | ||
cssModules({ | ||
mode: 'mixed', | ||
}), | ||
], | ||
... | ||
``` | ||
- Remove all `$style.` prefix from the html markup | ||
- Add the attribute `module` to `<style>` within your components. | ||
```html | ||
<style module> | ||
... | ||
</style> | ||
``` | ||
## Code Example | ||
@@ -1101,0 +1079,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
112569
3.64%1779
2.48%1154
-1.87%