or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

tessl/npm-sugarss

Indent-based CSS syntax parser for PostCSS that provides an alternative to traditional CSS syntax with braces and semicolons

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/sugarss@5.0.x

To install, run

npx @tessl/cli install tessl/npm-sugarss@5.0.0

0

# SugarSS

1

2

SugarSS is an indent-based CSS syntax parser for PostCSS that provides an alternative to traditional CSS syntax with braces and semicolons. It allows developers to write CSS using indentation similar to Sass or Stylus while maintaining full compatibility with PostCSS plugins and source maps.

3

4

## Package Information

5

6

- **Package Name**: sugarss

7

- **Package Type**: npm

8

- **Language**: JavaScript

9

- **Installation**: `npm install sugarss postcss`

10

11

## Core Imports

12

13

### ES Modules

14

15

```javascript

16

// Default import (contains parse and stringify)

17

import sugarss from "sugarss";

18

19

// Named imports

20

import { parse, stringify } from "sugarss";

21

22

// Direct module imports (as per package.json exports)

23

import parse from "sugarss/parse";

24

import stringify from "sugarss/stringify";

25

import tokenize from "sugarss/tokenize";

26

```

27

28

### CommonJS

29

30

```javascript

31

// Default require (object with parse and stringify)

32

const sugarss = require("sugarss");

33

const { parse, stringify } = sugarss;

34

35

// Destructured require

36

const { parse, stringify } = require("sugarss");

37

38

// Direct module requires (as per package.json exports)

39

const parse = require("sugarss/parse");

40

const stringify = require("sugarss/stringify");

41

const tokenize = require("sugarss/tokenize");

42

```

43

44

## Basic Usage

45

46

### As PostCSS Parser

47

48

```javascript

49

import postcss from "postcss";

50

import sugarss from "sugarss";

51

52

const sugarssInput = `

53

a

54

color: blue

55

font-size: 16px

56

57

.nested

58

.child

59

padding: 10px

60

`;

61

62

// Parse SugarSS to CSS

63

const result = await postcss()

64

.process(sugarssInput, { parser: sugarss });

65

66

console.log(result.css);

67

```

68

69

### As Complete PostCSS Syntax

70

71

```javascript

72

import postcss from "postcss";

73

import sugarss from "sugarss";

74

75

// Use as both parser and stringifier

76

const result = await postcss()

77

.process(sugarssInput, { syntax: sugarss });

78

```

79

80

### Convert CSS to SugarSS

81

82

```javascript

83

import postcss from "postcss";

84

import sugarss from "sugarss";

85

86

const cssInput = `

87

a {

88

color: blue;

89

font-size: 16px;

90

}

91

`;

92

93

// Convert CSS to SugarSS format

94

const result = await postcss()

95

.process(cssInput, { stringifier: sugarss });

96

97

console.log(result.css);

98

// Output:

99

// a

100

// color: blue

101

// font-size: 16px

102

```

103

104

## Architecture

105

106

SugarSS is built around several key components:

107

108

- **Parser Pipeline**: tokenize → liner → preprocess → parse workflow

109

- **PostCSS Integration**: Full compatibility with PostCSS's Input, AST nodes, and plugin system

110

- **Syntax Features**: Indentation-based nesting, multiline selectors/values, dual comment types

111

- **Error Handling**: Detailed parse errors with precise location information

112

- **Type Safety**: Works seamlessly with PostCSS's type system

113

114

## Capabilities

115

116

### Core Parser Function

117

118

Parses SugarSS syntax into PostCSS AST nodes for further processing.

119

120

```javascript { .api }

121

/**

122

* Parse SugarSS syntax into PostCSS AST

123

* @param source - SugarSS source code string

124

* @param opts - PostCSS Input options (from, map, etc.)

125

* @returns PostCSS Root node

126

*/

127

function parse(source: string, opts?: InputOptions): Root;

128

129

interface InputOptions {

130

from?: string;

131

map?: SourceMapOptions | boolean;

132

to?: string;

133

origin?: string;

134

}

135

136

interface SourceMapOptions {

137

inline?: boolean;

138

annotation?: boolean | string;

139

sourcesContent?: boolean;

140

from?: string;

141

to?: string;

142

}

143

144

interface Root {

145

type: 'root';

146

nodes: ChildNode[];

147

source?: NodeSource;

148

raws?: RootRaws;

149

walkRules(callback: (rule: Rule) => void): void;

150

walkDecls(callback: (decl: Declaration) => void): void;

151

walkAtRules(callback: (atrule: AtRule) => void): void;

152

walkComments(callback: (comment: Comment) => void): void;

153

}

154

155

interface NodeSource {

156

input: Input;

157

start?: Position;

158

end?: Position;

159

}

160

161

interface Position {

162

line: number;

163

column: number;

164

offset: number;

165

}

166

167

interface RootRaws {

168

indent?: string;

169

after?: string;

170

semicolon?: boolean;

171

}

172

173

type ChildNode = Rule | AtRule | Declaration | Comment;

174

```

175

176

**Usage Examples:**

177

178

```javascript

179

import { parse } from "sugarss";

180

import { Input } from "postcss";

181

182

// Basic parsing

183

const root = parse(`

184

a

185

color: red

186

font-size: 14px

187

`, { from: 'input.sss' });

188

189

// With source maps

190

const rootWithMap = parse(sugarssCode, {

191

from: 'styles.sss',

192

map: { inline: false }

193

});

194

195

// Access parsed nodes

196

root.walkRules(rule => {

197

console.log(rule.selector); // "a"

198

});

199

200

root.walkDecls(decl => {

201

console.log(decl.prop, decl.value); // "color", "red"

202

});

203

```

204

205

### Core Stringifier Function

206

207

Converts PostCSS AST nodes back to SugarSS syntax format.

208

209

```javascript { .api }

210

/**

211

* Stringify PostCSS AST to SugarSS format

212

* @param node - PostCSS AST node to stringify

213

* @param builder - PostCSS builder function for output

214

*/

215

function stringify(node: AnyNode, builder: Builder): void;

216

217

type Builder = (str: string, node?: AnyNode, type?: 'start' | 'end') => void;

218

219

interface Rule {

220

type: 'rule';

221

selector: string;

222

nodes: Declaration[];

223

source?: NodeSource;

224

raws?: RuleRaws;

225

}

226

227

interface AtRule {

228

type: 'atrule';

229

name: string;

230

params: string;

231

nodes?: ChildNode[];

232

source?: NodeSource;

233

raws?: AtRuleRaws;

234

}

235

236

interface Declaration {

237

type: 'decl';

238

prop: string;

239

value: string;

240

important?: boolean;

241

source?: NodeSource;

242

raws?: DeclRaws;

243

}

244

245

interface Comment {

246

type: 'comment';

247

text: string;

248

source?: NodeSource;

249

raws?: CommentRaws;

250

}

251

252

interface RuleRaws {

253

before?: string;

254

after?: string;

255

selector?: RawSelector;

256

}

257

258

interface AtRuleRaws {

259

before?: string;

260

after?: string;

261

afterName?: string;

262

params?: RawParams;

263

sssBetween?: string;

264

}

265

266

interface DeclRaws {

267

before?: string;

268

after?: string;

269

between?: string;

270

value?: RawValue;

271

}

272

273

interface CommentRaws {

274

before?: string;

275

after?: string;

276

left?: string;

277

right?: string;

278

}

279

280

interface RawSelector {

281

value: string;

282

raw: string;

283

}

284

285

interface RawParams {

286

value: string;

287

raw: string;

288

}

289

290

interface RawValue {

291

value: string;

292

raw: string;

293

}

294

295

type AnyNode = Root | AtRule | Rule | Declaration | Comment;

296

```

297

298

**Usage Examples:**

299

300

```javascript

301

import { stringify } from "sugarss";

302

import postcss from "postcss";

303

304

// Use as PostCSS stringifier

305

const result = await postcss()

306

.process(cssInput, {

307

parser: postcss.parse,

308

stringifier: stringify

309

});

310

311

// Direct usage with PostCSS process

312

const convertToCss = await postcss()

313

.process(sugarssInput, {

314

parser: parse,

315

stringifier: postcss.stringify

316

});

317

```

318

319

### Tokenizer Function

320

321

Low-level tokenization function that converts SugarSS source into structured tokens.

322

323

```javascript { .api }

324

/**

325

* Tokenize SugarSS source into structured tokens

326

* @param input - PostCSS Input object containing source

327

* @returns Array of tokens with position information

328

*/

329

function tokenize(input: Input): Token[];

330

331

interface Input {

332

css: string;

333

from?: string;

334

origin?: string;

335

error(message: string, offset: number): CssSyntaxError;

336

error(message: string, line: number, column: number): CssSyntaxError;

337

}

338

339

type Token = [

340

type: TokenType,

341

value: string,

342

startOffset: number,

343

endOffset: number,

344

...additional: any[]

345

];

346

347

type TokenType =

348

| 'newline' // \n, \r\n, \f, \r

349

| 'space' // spaces and tabs

350

| '{' // opening curly brace

351

| '}' // closing curly brace

352

| ':' // colon separator

353

| ';' // semicolon

354

| ',' // comma

355

| '(' // opening parenthesis

356

| ')' // closing parenthesis

357

| 'brackets' // matched parentheses content

358

| 'string' // quoted strings

359

| 'at-word' // @-rules like @media, @import

360

| 'word' // identifiers, values, selectors

361

| 'comment'; // /* */ and // comments

362

363

interface CssSyntaxError extends Error {

364

name: 'CssSyntaxError';

365

message: string;

366

file?: string;

367

line: number;

368

column: number;

369

source: string;

370

pos: number;

371

}

372

```

373

374

**Usage Examples:**

375

376

```javascript

377

import { tokenize } from "sugarss/tokenize";

378

import { Input } from "postcss";

379

380

const input = new Input(`

381

a

382

color: blue

383

`, { from: 'test.sss' });

384

385

const tokens = tokenize(input);

386

387

tokens.forEach(token => {

388

const [type, value, start, end] = token;

389

console.log(`${type}: "${value}" at ${start}-${end}`);

390

});

391

392

// Example output:

393

// newline: "\n" at 0-1

394

// word: "a" at 1-2

395

// newline: "\n" at 2-3

396

// space: " " at 3-5

397

// word: "color" at 5-10

398

// :: ":" at 10-11

399

// space: " " at 11-12

400

// word: "blue" at 12-16

401

```

402

403

### Syntax Features

404

405

SugarSS supports comprehensive CSS syntax with indentation-based structure:

406

407

#### Indentation Rules

408

409

```javascript

410

// ✅ Valid: Consistent 2-space indentation

411

const validIndent = `

412

.parent

413

color: blue

414

.child

415

padding: 10px

416

`;

417

418

// ❌ Invalid: Mixed tabs and spaces

419

const invalidMixed = `

420

.parent

421

color: blue // 2 spaces

422

\t.child // tab - will throw error

423

`;

424

425

// ❌ Invalid: First line cannot have indent

426

const invalidFirst = `

427

.parent // Error: First line should not have indent

428

color: blue

429

`;

430

```

431

432

#### Multiline Selectors and Values

433

434

```javascript

435

// Multiline selectors with consistent indentation

436

const multilineSelector = `

437

.parent >

438

.child,

439

.sibling

440

color: black

441

`;

442

443

// Multiline values with increased indentation

444

const multilineValue = `

445

.element

446

background:

447

linear-gradient(rgba(0, 0, 0, 0), black)

448

linear-gradient(red, rgba(255, 0, 0, 0))

449

box-shadow: 1px 0 9px rgba(0, 0, 0, .4),

450

1px 0 3px rgba(0, 0, 0, .6)

451

`;

452

453

// Continuation rules

454

const continuationRules = `

455

// 1. Brackets allow line breaks

456

@supports ( (display: flex) and

457

(display: grid) )

458

.flex-grid

459

display: flex

460

461

// 2. Comma at line end continues

462

@media (max-width: 400px),

463

(max-height: 800px)

464

.responsive

465

padding: 10px

466

467

// 3. Backslash before newline continues

468

@media screen and \\

469

(min-width: 600px)

470

.desktop

471

width: 100%

472

`;

473

```

474

475

#### Comment Types

476

477

```javascript

478

const comments = `

479

/*

480

Multiline comments

481

preserved in output

482

*/

483

484

.element

485

color: blue // Inline comments also preserved

486

487

// Standalone inline comment

488

.another

489

font-size: 16px

490

`;

491

```

492

493

### Integration with PostCSS Ecosystem

494

495

SugarSS works seamlessly with PostCSS plugins and tools:

496

497

```javascript

498

import postcss from "postcss";

499

import sugarss from "sugarss";

500

import autoprefixer from "autoprefixer";

501

import cssnano from "cssnano";

502

503

// Complete preprocessing pipeline

504

const result = await postcss([

505

autoprefixer(),

506

cssnano()

507

])

508

.process(sugarssInput, {

509

parser: sugarss,

510

from: 'src/styles.sss',

511

to: 'dist/styles.css'

512

});

513

514

// Configuration file usage (.postcssrc)

515

const config = {

516

"parser": "sugarss",

517

"plugins": {

518

"postcss-simple-vars": {},

519

"postcss-nested": {},

520

"autoprefixer": {}

521

}

522

};

523

```

524

525

### Error Handling

526

527

SugarSS provides detailed error messages with precise location information:

528

529

530

SugarSS uses PostCSS's standard error system, throwing `CssSyntaxError` instances:

531

532

```javascript

533

import { parse } from "sugarss";

534

535

// Indentation errors

536

try {

537

parse(`

538

.parent

539

color: blue

540

\t.child // Mixed tabs/spaces

541

padding: 10px

542

`);

543

} catch (error) {

544

console.log(error.name); // "CssSyntaxError"

545

console.log(error.message); // "Mixed tabs and spaces are not allowed"

546

console.log(`Line ${error.line}, Column ${error.column}`);

547

console.log(error.pos); // Character offset position

548

}

549

550

// Property syntax errors

551

try {

552

parse(`

553

.element

554

color:blue // Missing space after colon

555

`);

556

} catch (error) {

557

console.log(error.message);

558

// "Unexpected separator in property"

559

console.log(error.source); // Original source code

560

}

561

562

// Unclosed constructs

563

try {

564

parse(`

565

.element

566

content: "unclosed string

567

`);

568

} catch (error) {

569

console.log(error.message);

570

// "Unclosed quote"

571

console.log(error.file); // Input file path (if provided)

572

}

573

```

574

575

### Configuration and Options

576

577

SugarSS automatically detects and adapts to different indentation styles:

578

579

```javascript

580

// Auto-detection of indentation

581

const spacesInput = `

582

.element

583

color: blue // Detects 2-space indent

584

font-size: 16px // Nested with 4 spaces

585

`;

586

587

const tabsInput = `

588

.element

589

\tcolor: blue // Detects tab indent

590

\t\tfont-size: 16px // Nested with 2 tabs

591

`;

592

593

// Both parse correctly with auto-detection

594

const spacesRoot = parse(spacesInput);

595

const tabsRoot = parse(tabsInput);

596

597

console.log(spacesRoot.raws.indent); // " " (2 spaces)

598

console.log(tabsRoot.raws.indent); // "\t" (tab)

599

```

600

601

### Source Map Support

602

603

Full source map support for debugging and development tools:

604

605

```javascript

606

import { parse } from "sugarss";

607

608

const result = parse(sugarssCode, {

609

from: 'styles.sss',

610

map: {

611

inline: false,

612

annotation: true,

613

sourcesContent: true

614

}

615

});

616

617

// Source positions are preserved

618

result.walkDecls(decl => {

619

console.log(decl.source);

620

// {

621

// input: Input { css: '...', from: 'styles.sss' },

622

// start: { line: 3, column: 3, offset: 25 },

623

// end: { line: 3, column: 15, offset: 37 }

624

// }

625

});

626

```