refactor: split web frontend + gateway out to uniweb app (bump 0.13.0)

The SPA (web/) and the web gateway (cmd/webgw) move to a dedicated app
projects/message_bus/apps/uniweb (its own Gitea sub-repo). unibus is now
strictly the bus plane: membership/keys, the client library and demo peers.
uniweb consumes unibus as a Go module via replace => ../unibus.

No capability lost; same SPA and gateway, in their own service folder.
go build/vet/test green after extraction.
This commit is contained in:
2026-06-13 21:21:08 +02:00
parent fadee1a7d0
commit 9661a5ce1f
31166 changed files with 2029366 additions and 3677 deletions
+1
View File
@@ -0,0 +1 @@
../../postcss@8.5.15/node_modules/postcss
@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright 2016 Andrey Sitnik <andrey@sitnik.ru>
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,46 @@
# SugarSS
<img align="right" width="120" height="155"
title="SugarSS logo by Maria Keller"
src="http://postcss.github.io/sugarss/logo.svg">
Indent-based CSS syntax for [PostCSS].
```sass
a
color: blue
.multiline,
.selector
box-shadow: 1px 0 9px rgba(0, 0, 0, .4),
1px 0 3px rgba(0, 0, 0, .6)
// Mobile
@media (max-width: 400px)
.body
padding: 0 10px
```
As any PostCSS custom syntax, SugarSS has source map, [stylelint]
and [postcss-sorting] support out-of-box.
It was designed to be used with [postcss-simple-vars] and [postcss-nested].
But you can use it with any PostCSS plugins
or use it without any PostCSS plugins.
With [postcss-mixins] you can use `@mixin` syntax as in Sass.
<a href="https://evilmartians.com/?utm_source=sugarss">
<img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg"
alt="Sponsored by Evil Martians" width="236" height="54">
</a>
[postcss-mixins]: https://github.com/postcss/postcss-mixins
[postcss-nested]: https://github.com/postcss/postcss-nested
[postcss-simple-vars]: https://github.com/postcss/postcss-simple-vars
[postcss-sorting]: https://github.com/hudochenkov/postcss-sorting
[stylelint]: http://stylelint.io/
[PostCSS]: https://github.com/postcss/postcss
## Docs
Read full docs **[here](https://github.com/postcss/sugarss#readme)**.
@@ -0,0 +1,4 @@
let parse = require('./parse')
let stringify = require('./stringify')
module.exports = { parse, stringify }
@@ -0,0 +1,6 @@
import index from "./index.js"
export default index
export const stringify = index.stringify
export const parse = index.parse
@@ -0,0 +1,17 @@
module.exports = function liner(tokens) {
let line = []
let result = [line]
let brackets = 0
for (let token of tokens) {
line.push(token)
if (token[0] === '(') {
brackets += 1
} else if (token[0] === ')') {
brackets -= 1
} else if (token[0] === 'newline' && brackets === 0) {
line = []
result.push(line)
}
}
return result
}
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 68 KiB

@@ -0,0 +1,42 @@
{
"name": "sugarss",
"version": "5.0.1",
"description": "Indent-based CSS syntax for PostCSS",
"keywords": [
"css",
"postcss",
"postcss-syntax",
"syntax",
"indent",
"parser"
],
"author": "Andrey Sitnik <andrey@sitnik.ru>",
"license": "MIT",
"repository": "postcss/sugarss",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"engines": {
"node": ">=18.0"
},
"exports": {
".": {
"require": "./index.js",
"import": "./index.mjs"
},
"./parse": "./parse.js",
"./stringify": "./stringify.js",
"./tokenize": "./tokenize.js",
"./package.json": "./package.json"
},
"peerDependencies": {
"postcss": "^8.3.3"
}
}
@@ -0,0 +1,17 @@
let { Input } = require('postcss')
let liner = require('./liner')
let Parser = require('./parser')
let preprocess = require('./preprocess')
let tokenizer = require('./tokenize')
module.exports = function parse(source, opts) {
let input = new Input(source, opts)
let parser = new Parser(input)
parser.tokens = tokenizer(input)
parser.parts = preprocess(input, liner(parser.tokens))
parser.loop()
return parser.root
}
@@ -0,0 +1,402 @@
let { AtRule, Comment, Declaration, Root, Rule } = require('postcss')
module.exports = class Parser {
constructor(input) {
this.input = input
this.pos = 0
this.root = new Root()
this.current = this.root
this.spaces = ''
this.extraIndent = false
this.prevIndent = undefined
this.step = undefined
this.root.source = { input, start: { column: 1, line: 1, offset: 0 } }
}
atrule(part) {
let atword = part.tokens[0]
let params = part.tokens.slice(1)
let node = new AtRule()
node.name = atword[1].slice(1)
this.init(node, part)
if (node.name === '') this.unnamedAtrule(atword)
while (!part.end && part.lastComma) {
this.pos += 1
part = this.parts[this.pos]
params.push(['space', part.before + part.indent])
params = params.concat(part.tokens)
}
node.raws.afterName = this.firstSpaces(params)
this.keepTrailingSpace(node, params)
this.checkSemicolon(params)
this.checkCurly(params)
this.raw(node, 'params', params, atword)
}
badProp(token) {
let pos = this.getPosition(token[2])
this.error('Unexpected separator in property', pos.offset)
}
checkCurly(tokens) {
for (let token of tokens) {
if (token[0] === '{') {
let pos = this.getPosition(token[2])
this.error('Unnecessary curly bracket', pos.offset)
}
}
}
checkSemicolon(tokens) {
for (let token of tokens) {
if (token[0] === ';') {
let pos = this.getPosition(token[2])
this.error('Unnecessary semicolon', pos.offset)
}
}
}
comment(part) {
let token = part.tokens[0]
let node = new Comment()
this.init(node, part)
node.source.end = this.getPosition(token[3])
this.commentText(node, token)
}
/* Helpers */
commentText(node, token) {
let text = token[1]
if (token[4] === 'inline') {
node.raws.inline = true
text = text.slice(2)
} else {
text = text.slice(2, -2)
}
let match = text.match(/^(\s*)([^]*\S)(\s*)\n?$/)
if (match) {
node.text = match[2]
node.raws.left = match[1]
node.raws.inlineRight = match[3]
} else {
node.text = ''
node.raws.left = ''
node.raws.inlineRight = ''
}
}
decl(part) {
let node = new Declaration()
this.init(node, part)
let between = ''
let colon = 0
let value = []
let prop = ''
for (let i = 0; i < part.tokens.length; i++) {
let token = part.tokens[i]
if (token[0] === ':') {
between += token[1]
colon = token
value = part.tokens.slice(i + 1)
break
} else if (token[0] === 'comment' || token[0] === 'space') {
between += token[1]
} else if (between !== '') {
this.badProp(token)
} else {
prop += token[1]
}
}
if (prop === '') this.unnamedDecl(part.tokens[0])
node.prop = prop
let next = this.parts[this.pos + 1]
while (
!next.end &&
!next.atrule &&
!next.colon &&
next.indent.length > part.indent.length
) {
value.push(['space', next.before + next.indent])
value = value.concat(next.tokens)
this.pos += 1
next = this.parts[this.pos + 1]
}
let last = value[value.length - 1]
if (last && last[0] === 'comment') {
value.pop()
let comment = new Comment()
this.current.push(comment)
comment.source = {
end: this.getPosition(last[3]),
input: this.input,
start: this.getPosition(last[2])
}
let prev = value[value.length - 1]
if (prev && prev[0] === 'space') {
value.pop()
comment.raws.before = prev[1]
}
this.commentText(comment, last)
}
for (let i = value.length - 1; i > 0; i--) {
let t = value[i][0]
if (t === 'word' && value[i][1] === '!important') {
node.important = true
if (i > 0 && value[i - 1][0] === 'space') {
node.raws.important = value[i - 1][1] + '!important'
value.splice(i - 1, 2)
} else {
node.raws.important = '!important'
value.splice(i, 1)
}
break
} else if (t !== 'space' && t !== 'newline' && t !== 'comment') {
break
}
}
node.raws.between = between + this.firstSpaces(value)
this.checkSemicolon(value)
this.raw(node, 'value', value, colon)
}
error(msg, offset) {
let pos = this.getPosition(offset)
throw this.input.error(msg, pos.line, pos.column)
}
firstSpaces(tokens) {
let result = ''
for (let i = 0; i < tokens.length; i++) {
if (tokens[i][0] === 'space' || tokens[i][0] === 'newline') {
result += tokens.shift()[1]
i -= 1
} else {
break
}
}
return result
}
getPosition(offset) {
let pos = this.input.fromOffset(offset)
return {
column: pos.col,
line: pos.line,
offset
}
}
indent(part) {
let indent = part.indent.length
let isPrev = typeof this.prevIndent !== 'undefined'
if (!isPrev && indent) this.indentedFirstLine(part)
if (!this.step && indent) {
this.step = indent
this.root.raws.indent = part.indent
}
if (isPrev && this.prevIndent !== indent) {
let diff = indent - this.prevIndent
if (diff > 0) {
if (diff !== this.step) {
this.wrongIndent(this.prevIndent + this.step, indent, part)
} else if (this.current.last.push) {
this.current = this.current.last
} else {
this.extraIndent = ''
for (let i = 0; i < diff; i++) {
this.extraIndent += ' '
}
}
} else if (diff % this.step !== 0) {
let m = indent + (diff % this.step)
this.wrongIndent(`${m} or ${m + this.step}`, indent, part)
} else {
for (let i = 0; i < -diff / this.step; i++) {
this.current = this.current.parent
}
}
}
this.prevIndent = indent
}
indentedFirstLine(part) {
let pos = this.getPosition(part.tokens[0][2])
this.error('First line should not have indent', pos.offset)
}
init(node, part) {
this.indent(part)
if (!this.current.nodes) this.current.nodes = []
this.current.push(node)
node.raws.before = part.before + part.indent
if (this.extraIndent) {
node.raws.extraIndent = this.extraIndent
this.extraIndent = false
}
node.source = {
input: this.input,
start: this.getPosition(part.tokens[0][2])
}
}
keepTrailingSpace(node, tokens) {
let lastSpace = tokens[tokens.length - 1]
if (lastSpace && lastSpace[0] === 'space') {
tokens.pop()
node.raws.sssBetween = lastSpace[1]
}
}
loop() {
let part
while (this.pos < this.parts.length) {
part = this.parts[this.pos]
if (part.comment) {
this.comment(part)
} else if (part.atrule) {
this.atrule(part)
} else if (part.colon) {
let next = this.nextNonComment(this.pos)
if (next.end || next.atrule) {
this.decl(part)
} else {
let moreIndent = next.indent.length > part.indent.length
if (!moreIndent) {
this.decl(part)
} else if (moreIndent && next.colon) {
this.rule(part)
} else if (moreIndent && !next.colon) {
this.decl(part)
}
}
} else if (part.end) {
this.root.raws.after = part.before
} else {
this.rule(part)
}
this.pos += 1
}
for (let i = this.tokens.length - 1; i >= 0; i--) {
if (this.tokens[i].length > 3) {
let last = this.tokens[i]
this.root.source.end = this.getPosition(last[3])
break
}
}
}
// Errors
nextNonComment(pos) {
let next = pos
let part
while (next < this.parts.length) {
next += 1
part = this.parts[next]
if (part.end || !part.comment) break
}
return part
}
raw(node, prop, tokens, altLast) {
let token, type
let length = tokens.length
let value = ''
let clean = true
for (let i = 0; i < length; i += 1) {
token = tokens[i]
type = token[0]
if (type === 'comment' || (type === 'space' && i === length - 1)) {
clean = false
} else {
value += token[1]
}
}
if (!clean) {
let sss = tokens.reduce((all, i) => all + i[1], '')
let raw = tokens.reduce((all, i) => {
if (i[0] === 'comment' && i[4] === 'inline') {
return all + '/* ' + i[1].slice(2).trim() + ' */'
} else {
return all + i[1]
}
}, '')
node.raws[prop] = { raw, value }
if (sss !== raw) node.raws[prop].sss = sss
}
node[prop] = value
let last
for (let i = tokens.length - 1; i >= 0; i--) {
if (tokens[i].length > 2) {
last = tokens[i]
break
}
}
if (!last) last = altLast
node.source.end = this.getPosition(last[3])
}
rule(part) {
let node = new Rule()
this.init(node, part)
let selector = part.tokens
let next = this.parts[this.pos + 1]
while (!next.end && next.indent.length === part.indent.length) {
selector.push(['space', next.before + next.indent])
selector = selector.concat(next.tokens)
this.pos += 1
next = this.parts[this.pos + 1]
}
this.keepTrailingSpace(node, selector)
this.checkCurly(selector)
this.raw(node, 'selector', selector)
}
unnamedAtrule(token) {
let pos = this.getPosition(token[2])
this.error('At-rule without name', pos.offset)
}
unnamedDecl(token) {
let pos = this.getPosition(token[2])
this.error('Declaration without name', pos.offset)
}
wrongIndent(expected, real, part) {
let pos = this.getPosition(part.tokens[0][2])
let msg = `Expected ${expected} indent, but get ${real}`
this.error(msg, pos.offset)
}
}
@@ -0,0 +1,118 @@
function indentError(input, l, p) {
throw input.error('Mixed tabs and spaces are not allowed', l, p + 1)
}
module.exports = function preprocess(input, lines) {
let indentType
let prevNumber = 0
let parts = lines.map(line => {
let lastComma = false
let comment = false
let number = prevNumber + 1
let atrule = false
let indent = ''
let tokens = []
let colon = false
if (line.length > 0) {
if (line[0][0] === 'space') {
indent = line[0][1]
tokens = line.slice(1)
} else {
indent = ''
tokens = line
}
if (!indentType && indent.length) {
indentType = indent[0] === ' ' ? 'space' : 'tab'
}
if (indentType === 'space') {
if (indent.includes('\t')) {
indentError(input, number, indent.indexOf('\t'))
}
} else if (indentType === 'tab') {
if (indent.includes(' ')) {
indentError(input, number, indent.indexOf(' '))
}
}
if (tokens.length) {
for (let i = tokens.length - 1; i >= 0; i--) {
let type = tokens[i][0]
if (type === ',') {
lastComma = true
break
} else if (type === 'space') {
continue
} else if (type === 'comment') {
continue
} else if (type === 'newline') {
continue
} else {
break
}
}
comment = tokens[0][0] === 'comment'
atrule = tokens[0][0] === 'at-word'
let brackets = 0
for (let i = 0; i < tokens.length - 1; i++) {
let type = tokens[i][0]
let next = tokens[i + 1][0]
if (type === '(') {
brackets += 1
} else if (type === ')') {
brackets -= 1
} else if (
type === ':' &&
brackets === 0 &&
(next === 'space' || next === 'newline')
) {
colon = true
}
}
}
let last = tokens[tokens.length - 1]
if (last && last[0] === 'newline') prevNumber = last[2]
}
return {
atrule,
before: '',
colon,
comment,
indent,
lastComma,
number,
tokens
}
})
parts = parts.reduceRight(
(all, i) => {
if (!i.tokens.length || i.tokens.every(j => j[0] === 'newline')) {
let prev = all[0]
let before = i.indent + i.tokens.map(j => j[1]).join('')
prev.before = before + prev.before
} else {
all.unshift(i)
}
return all
},
[{ before: '', end: true }]
)
parts.forEach((part, i) => {
if (i === 0) return
let prev = parts[i - 1]
let last = prev.tokens[prev.tokens.length - 1]
if (last && last[0] === 'newline') {
part.before = last[1] + part.before
prev.tokens.pop()
}
})
return parts
}
@@ -0,0 +1,114 @@
const DEFAULT_RAWS = {
colon: ': ',
commentLeft: ' ',
commentRight: ' ',
indent: ' '
}
module.exports = class Stringifier {
constructor(builder) {
this.builder = builder
}
atrule(node) {
let name = '@' + node.name
let params = node.params ? this.rawValue(node, 'params') : ''
if (this.has(node.raws.afterName)) {
name += node.raws.afterName
} else if (params) {
name += ' '
}
this.block(node, name + params)
}
block(node, start) {
let between = node.raws.sssBetween || ''
this.builder(start + between, node, 'start')
if (this.has(node.nodes)) this.body(node)
}
body(node) {
let indent = node.root().raws.indent || DEFAULT_RAWS.indent
for (let i = 0; i < node.nodes.length; i++) {
let child = node.nodes[i]
let before =
child.raws.before.replace(/[^\n]*$/, '') + this.indent(node, indent)
if (child.type === 'comment' && !child.raws.before.includes('\n')) {
before = child.raws.before
}
if (before) this.builder(before)
this.stringify(child)
}
}
comment(node) {
let left = DEFAULT_RAWS.commentLeft
let right = DEFAULT_RAWS.commentRight
if (this.has(node.raws.left)) left = node.raws.left
if (node.raws.inline) {
if (this.has(node.raws.inlineRight)) {
right = node.raws.inlineRight
} else {
right = ''
}
if (node.raws.extraIndent) {
this.builder(node.raws.extraIndent)
}
this.builder('//' + left + node.text + right, node)
} else {
if (this.has(node.raws.right)) right = node.raws.right
this.builder('/*' + left + node.text + right + '*/', node)
}
}
decl(node) {
let between = node.raws.between || DEFAULT_RAWS.colon
let string = node.prop + between + this.rawValue(node, 'value')
if (node.important) {
string += node.raws.important || ' !important'
}
this.builder(string, node)
}
has(value) {
return typeof value !== 'undefined'
}
indent(node, step) {
let result = ''
while (node.parent) {
result += step
node = node.parent
}
return result
}
rawValue(node, prop) {
let value = node[prop]
let raw = node.raws[prop]
if (raw && raw.value === value) {
return raw.sss || raw.raw
} else {
return value
}
}
root(node) {
this.body(node)
if (node.raws.after) this.builder(node.raws.after)
}
rule(node) {
this.block(node, this.rawValue(node, 'selector'))
}
stringify(node, semicolon) {
this[node.type](node, semicolon)
}
}
@@ -0,0 +1,6 @@
let Stringifier = require('./stringifier')
module.exports = function stringify(node, builder) {
let str = new Stringifier(builder)
str.stringify(node)
}
@@ -0,0 +1,282 @@
const SINGLE_QUOTE = "'".charCodeAt(0)
const DOUBLE_QUOTE = '"'.charCodeAt(0)
const BACKSLASH = '\\'.charCodeAt(0)
const SLASH = '/'.charCodeAt(0)
const NEWLINE = '\n'.charCodeAt(0)
const SPACE = ' '.charCodeAt(0)
const FEED = '\f'.charCodeAt(0)
const TAB = '\t'.charCodeAt(0)
const CR = '\r'.charCodeAt(0)
const OPEN_PARENTHESES = '('.charCodeAt(0)
const CLOSE_PARENTHESES = ')'.charCodeAt(0)
const OPEN_CURLY = '{'.charCodeAt(0)
const CLOSE_CURLY = '}'.charCodeAt(0)
const SEMICOLON = ';'.charCodeAt(0)
const ASTERICK = '*'.charCodeAt(0)
const COLON = ':'.charCodeAt(0)
const AT = '@'.charCodeAt(0)
const COMMA = ','.charCodeAt(0)
const RE_AT_END = /[\t\n\f\r "'()/;\\{]/g
const RE_NEW_LINE = /[\n\f\r]/g
const RE_WORD_END = /[\t\n\f\r !"'(),:;@\\{}]|\/(?=\*)/g
const RE_BAD_BRACKET = /.[\n"'(/\\]/
module.exports = function tokenize(input) {
let tokens = []
let css = input.css.valueOf()
let code, content, escape, escaped, escapePos, n, next, prev, quote
let length = css.length
let offset = 0
let pos = 0
function unclosed(what) {
throw input.error('Unclosed ' + what, pos)
}
while (pos < length) {
code = css.charCodeAt(pos)
switch (code) {
case CR:
if (css.charCodeAt(pos + 1) === NEWLINE) {
tokens.push(['newline', '\r\n', offset, offset + 2])
offset += 2
pos += 1
} else {
tokens.push(['newline', '\r', offset, offset + 1])
offset++
}
break
case NEWLINE:
case FEED:
tokens.push(['newline', css.slice(pos, pos + 1), offset, offset + 1])
offset++
break
case SPACE:
case TAB:
next = pos
do {
next += 1
code = css.charCodeAt(next)
} while (code === SPACE || code === TAB)
tokens.push([
'space',
css.slice(pos, next),
offset,
offset + (next - pos)
])
offset += next - pos
pos = next - 1
break
case OPEN_CURLY:
tokens.push(['{', '{', offset, offset + 1])
offset++
break
case CLOSE_CURLY:
tokens.push(['}', '}', offset, offset + 1])
offset++
break
case COLON:
tokens.push([':', ':', offset, offset + 1])
offset++
break
case SEMICOLON:
tokens.push([';', ';', offset, offset + 1])
offset++
break
case COMMA:
tokens.push([',', ',', offset, offset + 1])
offset++
break
case OPEN_PARENTHESES:
prev = tokens.length ? tokens[tokens.length - 1][1] : ''
n = css.charCodeAt(pos + 1)
if (
prev === 'url' &&
n !== SINGLE_QUOTE &&
n !== DOUBLE_QUOTE &&
n !== SPACE &&
n !== NEWLINE &&
n !== TAB &&
n !== FEED &&
n !== CR
) {
next = pos
do {
escaped = false
next = css.indexOf(')', next + 1)
if (next === -1) unclosed('bracket')
escapePos = next
while (css.charCodeAt(escapePos - 1) === BACKSLASH) {
escapePos -= 1
escaped = !escaped
}
} while (escaped)
tokens.push(['brackets', css.slice(pos, next + 1), offset, next + 1])
offset = next + 1
pos = next
} else {
next = css.indexOf(')', pos + 1)
content = css.slice(pos, next + 1)
if (next === -1 || RE_BAD_BRACKET.test(content)) {
tokens.push(['(', '(', offset, offset + 1])
offset++
} else {
tokens.push(['brackets', content, offset, next + 1])
offset = next + 1
pos = next
}
}
break
case CLOSE_PARENTHESES:
tokens.push([')', ')', offset, offset + 1])
offset++
break
case SINGLE_QUOTE:
case DOUBLE_QUOTE:
quote = code === SINGLE_QUOTE ? "'" : '"'
next = pos
do {
escaped = false
next = css.indexOf(quote, next + 1)
if (next === -1) unclosed('quote')
escapePos = next
while (css.charCodeAt(escapePos - 1) === BACKSLASH) {
escapePos -= 1
escaped = !escaped
}
} while (escaped)
content = css.slice(pos, next + 1)
tokens.push(['string', content, offset, offset + content.length])
offset += content.length
pos = next
break
case AT:
RE_AT_END.lastIndex = pos + 1
RE_AT_END.test(css)
if (RE_AT_END.lastIndex === 0) {
next = css.length - 1
} else {
next = RE_AT_END.lastIndex - 2
}
tokens.push([
'at-word',
css.slice(pos, next + 1),
offset,
offset + (next - pos + 1)
])
offset += next - pos + 1
pos = next
break
case BACKSLASH:
next = pos
escape = true
while (css.charCodeAt(next + 1) === BACKSLASH) {
next += 1
escape = !escape
}
code = css.charCodeAt(next + 1)
if (escape) {
if (code === CR && css.charCodeAt(next + 2) === NEWLINE) {
next += 2
} else if (code === CR || code === NEWLINE || code === FEED) {
next += 1
} else {
next += 1
}
}
tokens.push([
'word',
css.slice(pos, next + 1),
offset,
offset + (next - pos + 1)
])
offset += next - pos + 1
pos = next
break
default:
n = css.charCodeAt(pos + 1)
if (code === SLASH && n === ASTERICK) {
next = css.indexOf('*/', pos + 2) + 1
if (next === 0) unclosed('comment')
content = css.slice(pos, next + 1)
tokens.push(['comment', content, offset, offset + content.length])
offset += content.length
pos = next
} else if (code === SLASH && n === SLASH) {
RE_NEW_LINE.lastIndex = pos + 1
RE_NEW_LINE.test(css)
if (RE_NEW_LINE.lastIndex === 0) {
next = css.length - 1
} else {
next = RE_NEW_LINE.lastIndex - 2
}
content = css.slice(pos, next + 1)
tokens.push([
'comment',
content,
offset,
offset + content.length,
'inline'
])
offset += content.length
pos = next
} else {
RE_WORD_END.lastIndex = pos + 1
RE_WORD_END.test(css)
if (RE_WORD_END.lastIndex === 0) {
next = css.length - 1
} else {
next = RE_WORD_END.lastIndex - 2
}
content = css.slice(pos, next + 1)
tokens.push(['word', content, offset, offset + content.length])
offset += content.length
pos = next
}
break
}
pos++
}
return tokens
}