Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
549 changes: 527 additions & 22 deletions src/parse/index.ts

Large diffs are not rendered by default.

216 changes: 216 additions & 0 deletions src/stringify/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ import {
type CssCommentAST,
type CssCommonPositionAST,
type CssContainerAST,
type CssCounterStyleAST,
type CssCustomMediaAST,
type CssDeclarationAST,
type CssDocumentAST,
type CssFontFaceAST,
type CssFontFeatureValuesAST,
type CssGenericAtRuleAST,
type CssHostAST,
type CssImportAST,
type CssKeyframeAST,
Expand All @@ -16,11 +19,16 @@ import {
type CssMediaAST,
type CssNamespaceAST,
type CssPageAST,
type CssPageMarginBoxAST,
type CssPositionTryAST,
type CssPropertyAST,
type CssRuleAST,
type CssScopeAST,
type CssStartingStyleAST,
type CssStylesheetAST,
type CssSupportsAST,
CssTypes,
type CssViewTransitionAST,
} from '../type';

export type CompilerOptions = {
Expand Down Expand Up @@ -76,12 +84,16 @@ class Compiler {
return this.container(node);
case CssTypes.charset:
return this.charset(node);
case CssTypes.counterStyle:
return this.counterStyle(node);
case CssTypes.document:
return this.document(node);
case CssTypes.customMedia:
return this.customMedia(node);
case CssTypes.fontFace:
return this.fontFace(node);
case CssTypes.fontFeatureValues:
return this.fontFeatureValues(node);
case CssTypes.host:
return this.host(node);
case CssTypes.import:
Expand All @@ -98,10 +110,22 @@ class Compiler {
return this.namespace(node);
case CssTypes.page:
return this.page(node);
case CssTypes.pageMarginBox:
return this.pageMarginBox(node);
case CssTypes.positionTry:
return this.positionTry(node);
case CssTypes.property:
return this.property(node);
case CssTypes.scope:
return this.scope(node);
case CssTypes.startingStyle:
return this.startingStyle(node);
case CssTypes.supports:
return this.supports(node);
case CssTypes.viewTransition:
return this.viewTransition(node);
case CssTypes.atRule:
return this.genericAtRule(node);
}
}

Expand Down Expand Up @@ -362,6 +386,28 @@ class Compiler {
);
}

/**
* Visit @page margin box node (@top-left, @bottom-right, etc.).
*/
pageMarginBox(node: CssPageMarginBoxAST) {
if (this.compress) {
return (
this.emit(`@${node.name}`, node.position) +
this.emit('{') +
this.mapVisit(node.declarations) +
this.emit('}')
);
}
return (
this.emit(`${this.indent()}@${node.name} `, node.position) +
this.emit('{\n') +
this.emit(this.indent(1)) +
this.mapVisit(node.declarations, '\n') +
this.emit(this.indent(-1)) +
this.emit(`\n${this.indent()}}`)
);
}

/**
* Visit font-face node.
*/
Expand Down Expand Up @@ -414,6 +460,176 @@ class Compiler {
);
}

/**
* Visit @property node.
*/
property(node: CssPropertyAST) {
if (this.compress) {
return (
this.emit(`@property ${node.name}`, node.position) +
this.emit('{') +
this.mapVisit(node.declarations) +
this.emit('}')
);
}
return (
this.emit(`@property ${node.name} `, node.position) +
this.emit('{\n') +
this.emit(this.indent(1)) +
this.mapVisit(node.declarations, '\n') +
this.emit(this.indent(-1)) +
this.emit('\n}')
);
}

/**
* Visit @counter-style node.
*/
counterStyle(node: CssCounterStyleAST) {
if (this.compress) {
return (
this.emit(`@counter-style ${node.name}`, node.position) +
this.emit('{') +
this.mapVisit(node.declarations) +
this.emit('}')
);
}
return (
this.emit(`@counter-style ${node.name} `, node.position) +
this.emit('{\n') +
this.emit(this.indent(1)) +
this.mapVisit(node.declarations, '\n') +
this.emit(this.indent(-1)) +
this.emit('\n}')
);
}

/**
* Visit @font-feature-values node.
*/
fontFeatureValues(node: CssFontFeatureValuesAST) {
if (this.compress) {
return (
this.emit(`@font-feature-values ${node.fontFamily}`, node.position) +
this.emit('{') +
this.mapVisit(node.rules) +
this.emit('}')
);
}
return (
this.emit(
`${this.indent()}@font-feature-values ${node.fontFamily}`,
node.position,
) +
this.emit(` {\n${this.indent(1)}`) +
this.mapVisit(node.rules, '\n\n') +
this.emit(`\n${this.indent(-1)}${this.indent()}}`)
);
}

/**
* Visit @scope node.
*/
scope(node: CssScopeAST) {
const prelude = node.scope ? ` ${node.scope}` : '';
if (this.compress) {
return (
this.emit(`@scope${prelude}`, node.position) +
this.emit('{') +
this.mapVisit(node.rules) +
this.emit('}')
);
}
return (
this.emit(`${this.indent()}@scope${prelude}`, node.position) +
this.emit(` {\n${this.indent(1)}`) +
this.mapVisit(node.rules, '\n\n') +
this.emit(`\n${this.indent(-1)}${this.indent()}}`)
);
}

/**
* Visit @view-transition node.
*/
viewTransition(node: CssViewTransitionAST) {
if (this.compress) {
return (
this.emit('@view-transition', node.position) +
this.emit('{') +
this.mapVisit(node.declarations) +
this.emit('}')
);
}
return (
this.emit('@view-transition ', node.position) +
this.emit('{\n') +
this.emit(this.indent(1)) +
this.mapVisit(node.declarations, '\n') +
this.emit(this.indent(-1)) +
this.emit('\n}')
);
}

/**
* Visit @position-try node.
*/
positionTry(node: CssPositionTryAST) {
if (this.compress) {
return (
this.emit(`@position-try ${node.name}`, node.position) +
this.emit('{') +
this.mapVisit(node.declarations) +
this.emit('}')
);
}
return (
this.emit(`@position-try ${node.name} `, node.position) +
this.emit('{\n') +
this.emit(this.indent(1)) +
this.mapVisit(node.declarations, '\n') +
this.emit(this.indent(-1)) +
this.emit('\n}')
);
}

/**
* Visit generic at-rule node (fallback for any unrecognized at-rule).
*/
genericAtRule(node: CssGenericAtRuleAST) {
const prelude = node.prelude ? ` ${node.prelude}` : '';
if (this.compress) {
return (
this.emit(`@${node.name}${prelude}`, node.position) +
(node.rules
? this.emit('{') +
this.mapVisit(<CssAllNodesAST[]>node.rules) +
this.emit('}')
: ';')
);
}
if (!node.rules) {
return this.emit(
`${this.indent()}@${node.name}${prelude};`,
node.position,
);
}
const hasNestedRules = node.rules.some(
(r) => r.type !== CssTypes.declaration && r.type !== CssTypes.comment,
);
const delim = hasNestedRules ? '\n\n' : '\n';
return (
this.emit(`${this.indent()}@${node.name}${prelude}`, node.position) +
this.emit(hasNestedRules ? ` {\n${this.indent(1)}` : ' {\n') +
this.emit(hasNestedRules ? '' : this.indent(1)) +
this.mapVisit(<CssAllNodesAST[]>node.rules, delim) +
this.emit(
hasNestedRules
? `\n${this.indent(-1)}${this.indent()}}`
: `${this.indent(-1)}\n${this.indent()}}`,
)
);
}

/**
* Visit rule node.
*/
Expand Down
Loading
Loading