Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- {isValue, notValue, assertType} = require 'sass-lexer/utils'
- tokenize = require 'sass-lexer'
- path = require 'path'
- createScope = ->
- mixins: []
- functions: []
- variables: []
- pushUnique = (arr, val) ->
- arr.push val if arr.indexOf(val) is -1
- # Parse the given module for its external references and top-level declarations.
- module.exports = (mod) ->
- # TODO: support .sass modules
- if mod.ext is '.sass'
- throw Error '.sass modules are not supported (yet)'
- code = mod.content
- parser = tokenize code
- depth = 0 # scope index
- scopes = [
- createScope() # top-level
- ]
- # external references (variables, functions, mixins)
- refs = createScope()
- # top-level declarations (currently unused)
- decls =
- mixins: {}
- functions: {}
- variables: {}
- # current top-level declaration (currently unused)
- decl = null
- offset = 0 # current char index
- prev = null # previous token
- curr = null # current token
- next = ->
- if prev = curr
- offset += prev[2]
- curr = parser.next()
- skip = (type) ->
- assertType next(), type
- next()
- # Look for the given value in every scope.
- search = (key, value) ->
- i = depth; while i >= 0
- return true if ~scopes[i--][key].indexOf value
- return false
- addReference = (tok) ->
- if !search (key = tok[0] + 's'), tok[1]
- pushUnique refs[key], tok[1]
- parseInclude = ->
- skip 'space'
- if curr and curr[0] is 'identifier'
- curr[0] = 'mixin'
- addReference curr
- parseClosure = ->
- type = curr[1]
- start = offset
- console.log type, start
- skip 'space'
- if curr and curr[0] is 'identifier'
- pushUnique scopes[depth][type + 's'], curr[1]
- parseArguments scope = createScope()
- if depth is 0
- scope.id = curr[1]
- scope.type = type
- scope.start = start
- while next()
- if curr[1] is '{'
- scopes[++depth] = scope
- return
- parseArguments = (scope) ->
- if !isValue parser.peek(), '('
- return false
- next()
- if isValue parser.peek(), ')'
- next()
- return true
- parseList (curr) ->
- if curr[0] is 'identifier'
- # function call
- if parseArguments()
- curr[0] = 'function'
- addReference curr
- # argument name
- else if curr[0] is 'variable'
- scope?.variables.push curr[1]
- if isValue parser.peek(), ':'
- next()
- while next()
- return if curr[1] is ','
- parseExpression curr
- return if isValue parser.peek(), ')'
- parseList = (each) ->
- open = 1 # unclosed () pairs
- while next()
- if curr[1] is '('
- open += 1 # maps, lists, and function calls
- else if curr[1] is ')'
- return true if --open is 0
- else each curr
- parser.err 'Unexpected end of file'
- parseInterpolation = ->
- while notValue next(), '}'
- parseExpression()
- return
- parseBlock = ->
- while next()
- return if curr[1] is ';'
- if curr[1] isnt '{'
- parseExpression()
- else
- scopes[++depth] = createScope()
- return
- parseLoop = ->
- declaring = true
- scope = createScope()
- while next()
- if curr[1] is '{'
- scopes[++depth] = scope
- return
- # variables before "in" are declarations
- if declaring
- if curr[1] is 'in'
- declaring = false
- else if curr[0] is 'variable'
- scope.variables.push curr[1]
- # variable reference
- else if curr[0] is 'variable'
- addReference curr
- else if curr[0] is 'identifier'
- # function call
- if parseArguments()
- curr[0] = 'function'
- addReference curr
- inSelector = ->
- i = -1
- while tok = parser.peek ++i
- return true if tok[1] is '{'
- break if tok[1] is ';'
- return false
- parseExpression = ->
- # map or list
- if curr[1] is '('
- return parseList parseExpression
- # interpolation
- if curr[1] is '#' and isValue parser.peek(), '{'
- return next() and parseInterpolation()
- switch curr[0]
- when 'variable'
- # declaration
- if isValue parser.peek(), ':'
- pushUnique scopes[depth].variables, curr[1]
- # continue until the end of declaration
- start = offset
- parseExpression() while notValue next(), ';'
- # top-level declaration
- if depth is 0
- decls.variables[curr[1]] = code.slice start, offset + 1
- # variable reference
- else addReference curr
- when 'identifier'
- # bail early for properties
- if isValue parser.peek(), ':'
- return next()
- if inSelector()
- # pseudo selector
- if isValue parser.peek(), '('
- next() and parseList parseExpression
- # function call
- else if parseArguments()
- curr[0] = 'function'
- addReference curr
- while next()
- if curr[1] is '{'
- # enter a new scope
- scopes[++depth] = createScope()
- else if curr[1] is '}'
- continue if depth is 0
- scope = scopes[depth]
- if scope.type
- decls[scope.type + 's'][scope.id] =
- code.slice scope.start, offset + 1
- # exit the current scope
- scopes.length = depth--
- else if curr[0] is 'atrule'
- switch curr[1]
- when 'include'
- parseInclude()
- when 'mixin', 'function'
- parseClosure()
- when 'for', 'each'
- parseLoop()
- else
- parseBlock()
- else parseExpression()
- mod.refs = refs
- mod.decls = scopes[0]
- mod
Add Comment
Please, Sign In to add comment