Skip to content

Commit

Permalink
Configuration directives
Browse files Browse the repository at this point in the history
  • Loading branch information
wcjohnson committed Jul 6, 2017
1 parent 8d40080 commit 5db0616
Show file tree
Hide file tree
Showing 10 changed files with 150 additions and 34 deletions.
66 changes: 45 additions & 21 deletions src/config.lsc
Original file line number Diff line number Diff line change
Expand Up @@ -106,18 +106,15 @@ export locatePluginOpts(opts, thisPlugin) ->

{}

eatWhitespace(code) ->
code.trim()

eatLineComment(code) ->
eatFluff(code) ->
now code = code.trim()
matches = code.match(/^(?:\/\/[^\r\n]*\r?\n?\s*)+([^]*)$/)
if matches: matches[1] else: code

eatBlockComment(code) ->
matches = code.match(/^(?:\/\*[^]*?\*\/\s*)+([^]*)$/)
if matches: matches[1] else: code
if matches: matches[1] else:
lcMatches = code.match(/^(?:\/\*[^]*?\*\/\s*)+([^]*)$/)
if lcMatches: lcMatches[1] else: code

eatDirective(code, directives) ->
now code = code.trim()
matches = code.match(/^'((?:[^'\\]|\\.)*)'\s*;?\s*([^]*)$/)
if matches:
directives.push(matches[1])
Expand All @@ -130,16 +127,43 @@ eatDirective(code, directives) ->
else:
code

eatLeadingComments(code) ->
let nextCode
// XXX: This is somewhat hackish. We can probably get Babylon to just parse the
// directives off the Program block as a plugin or something.
getDirectives(code) ->
directives = []
let before = code, after
while true:
now nextCode = eatWhitespace(nextCode)
now nextCode = eatBlockComment(nextCode)
now nextCode = eatWhitespace(nextCode)
now nextCode = eatLineComment(nextCode)
if nextCode == code: break
nextCode


getLeadingDirectives(code) ->
now code = eatLeadingComments(code)
now after = eatDirective(eatFluff(before), directives)
if after == before: break
now before = after
directives

getClauseOptions(opts, clause) ->
now clause = clause.split(':')
if clause.length == 2:
opt = clause[0].trim()
val = clause[1].trim()
if opt.length and val.length:
try:
opts[opt] = JSON.parse(val)
catch err:
return
elif clause.length == 1:
opt = clause[0].trim()
if opt.length: opts[opt] = true

getWithOptions(opts, wc) ->
clauses = wc.split(',')
for elem clause in clauses:
getClauseOptions(opts, clause)

export getOptionsForConfigurationDirectives(code) ->
result = {}
directives = getDirectives(code)
for elem directive in directives:
matches = directive.match(/^use \@oigroup\/lightscript\s*(?:with ([^]*))?$/)
if matches:
result.isLightScript = true
if matches[1]: getWithOptions(result, matches[1])

result
59 changes: 46 additions & 13 deletions src/index.lsc
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { replaceWithInlinedOperator } from "./inlinedOperators";
import { transformForInArrayStatement, transformForInObjectStatement, lintForInArrayStatement, lintForInObjectStatement } from "./for";
import { resetHelpers } from "./helpers";
import { markIdentifier } from "./stdlib";
import { locatePluginOpts, getParserOpts } from './config'
import { locatePluginOpts, getParserOpts, getOptionsForConfigurationDirectives } from './config'
import { getFileTypeInfo, createCompilerState, initializeCompilerState, postprocess } from './compilerState'

Lightscript(babel) ->
Expand Down Expand Up @@ -269,19 +269,52 @@ Lightscript(babel) ->
Program
}

computeParserOpts(file) ->
// By this time, we have access to the code of the file.
// We can parse shebang, directives, etc and manipulate parser options
// based on those data.
file.parseShebang()
{ opts, parserOpts } = file
fileTypeInfo = getFileTypeInfo(file)
pluginOpts = locatePluginOpts(opts, plugin) or {} // XXX: see comments below
cdOpts = getOptionsForConfigurationDirectives(file.code)

// Don't read as LSC
if (not fileTypeInfo.isLightScript) and (not cdOpts.isLightScript): return

Object.assign(pluginOpts, cdOpts)
console.dir(pluginOpts)

// Get LSC parser opts
opts.parserOpts = opts.parserOpts or {}
nextParserOpts = getParserOpts(pluginOpts, opts.parserOpts)
parserOpts.parse = nextParserOpts.parse
parserOpts.plugins = nextParserOpts.plugins


plugin = {
manipulateOptions(opts, parserOpts, file) {
fileTypeInfo = getFileTypeInfo(file)
if not fileTypeInfo.isLightScript: return

pluginOpts = locatePluginOpts(opts, plugin)

// Set parser to LightScript mode
opts.parserOpts = opts.parserOpts or {}
nextParserOpts = getParserOpts(pluginOpts, opts.parserOpts)
parserOpts.parse = nextParserOpts.parse
parserOpts.plugins = nextParserOpts.plugins
}
manipulateOptions(opts, parserOpts, file) ->
// This entire thing is a workaround for what is IMO a babel design flaw.
// Relevant Babel code:
// https://github.com/babel/babel/blob/4e50b2d9d9c376cee7a2cbf56553fe5b982ea53c/packages/babel-core/src/transformation/file/index.js#L63
// https://github.com/babel/babel/blob/4e50b2d9d9c376cee7a2cbf56553fe5b982ea53c/packages/babel-core/src/transformation/file/index.js#L483
//
// manipulateOptions is called very early in the Babel processing chain.
// It doesn't get an opportunity to see the configuration options for the
// plugin being run (without a workaround above) nor does it get an
// opportunity to see the code that will be compiled.
//
// This rules out things like parsing configuration directives from the
// top of a JS file to manipulate parsing and compiling options.
//
// This monkey patch defers the option manipulation until after the file
// has been read, but before actual parsing.
origAddCode = file.addCode

file.addCode(code) ->
origAddCode.call(this, code)
computeParserOpts(file)
return

visitor
}
Expand Down
2 changes: 2 additions & 0 deletions test/fixtures/configuration-directives/basic/actual.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
'use @oigroup/lightscript'
f() -> 1
5 changes: 5 additions & 0 deletions test/fixtures/configuration-directives/basic/expected.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
'use @oigroup/lightscript';

function f() {
return 1;
}
20 changes: 20 additions & 0 deletions test/fixtures/configuration-directives/kitchen-sink/actual.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// row
//row
/* row your boat
// gently
downTheStream*/
'merrily'
// merrilly
/* lifeIs
* butA
*/
'dream'
'use @oigroup/lightscript with bangCall'
/* and then */



// we do
'use @oigroup/lightscript with safeCall'

what?!
18 changes: 18 additions & 0 deletions test/fixtures/configuration-directives/kitchen-sink/expected.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// row
//row
/* row your boat
// gently
downTheStream*/
'merrily';
// merrilly
/* lifeIs
* butA
*/
'dream';
'use @oigroup/lightscript with bangCall';
/* and then */

// we do
'use @oigroup/lightscript with safeCall';

typeof what === 'function' ? what() : null;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// comment
f() -> x
'use @oigroup/lightscript with safeCall'
f?()
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"throws": "Unexpected token (4:3)"
}
2 changes: 2 additions & 0 deletions test/fixtures/configuration-directives/use-strict/actual.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
'use strict'
f() -> 1
5 changes: 5 additions & 0 deletions test/fixtures/configuration-directives/use-strict/expected.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
'use strict';

function f() {
return 1;
}

0 comments on commit 5db0616

Please sign in to comment.