riot_js-rails 0.6.2 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5209662e90ea7c75f0e12cfb17dd327504638d01
4
- data.tar.gz: 49c4adcf8d1244370fc76f1cbdfbb520bb47fa31
3
+ metadata.gz: 7f1f3ab924a0ddd2a63b4a9d73614ab08a2792a9
4
+ data.tar.gz: 07c38e96e874e3c56b6f7ddae718680ab7b569bd
5
5
  SHA512:
6
- metadata.gz: 7cbe0afdfe02b06dc7145d875e3e677b926e6cb43345f710e96881a66058a4e74ed25a472dd8b14485a876e48e4bc4df56efc421553b80ffa8e5abf403cd9f50
7
- data.tar.gz: 451bceae8a8539a844d9ebcb3b5bd64c16f8c2e24c4337bcfc4f5d81dc1597e89a28e147464c6174f51ec2b8a3776dcd57697e12043b363bbf1098faa443d0aa
6
+ metadata.gz: 6eb2e719eebf5c16705da093042c667b4d88bdc438875adffb4cc54208d827509ba6c2c57fa4a8c1ac464980d973bb37c168fd2b03e5bd25363ff239303abd24
7
+ data.tar.gz: d0a18d5eeb06494ad0c28bba921f814cb4c9b24e73040a8fe7d4b82f1a60eb792ec29b71072379303fe0b45f40a418bc76776170b39d6ac4832555f7a687a1e7
data/Rakefile CHANGED
@@ -7,4 +7,12 @@ Rake::TestTask.new do |t|
7
7
  end
8
8
 
9
9
  desc "Run tests"
10
- task :default => :test
10
+ task :default => :test
11
+
12
+ namespace :test do
13
+ desc "Test with various versions of sprockets"
14
+ task :sprockets_versions do
15
+ sh "bash test/test_sprockets_versions.sh"
16
+ end
17
+ end
18
+
@@ -1,23 +1,103 @@
1
1
  require 'riot_js/rails/processors/compiler'
2
2
 
3
- if Gem::Version.new(Sprockets::VERSION) < Gem::Version.new('3.0.0')
4
- require 'riot_js/rails/processors/sprockets_processor_v2'
5
- else
6
- require 'riot_js/rails/processors/sprockets_processor_v3'
7
- end
8
-
9
3
  module RiotJs
10
4
  module Rails
11
- class Processor < SprocketsProcessor
12
5
 
13
- def process
14
- compile_tag
6
+ # Sprockets 2, 3 & 4 interface
7
+ class SprocketsExtensionBase
8
+ attr_reader :default_mime_type
9
+
10
+ def initialize(filename, &block)
11
+ @filename = filename
12
+ @source = block.call
13
+ end
14
+
15
+ def render(context, empty_hash_wtf)
16
+ self.class.run(@filename, @source, context)
17
+ end
18
+
19
+ def self.run(filename, source, context)
20
+ raise 'Not implemented'
21
+ end
22
+
23
+ def self.call(input)
24
+ if input.is_a?(String)
25
+ run("", input, nil)
26
+ else
27
+ filename = input[:filename]
28
+ source = input[:data]
29
+ context = input[:environment].context_class.new(input)
30
+
31
+ result = run(filename, source, context)
32
+ context.metadata.merge(data: result)
33
+ end
15
34
  end
16
35
 
17
36
  private
18
37
 
19
- def compile_tag
20
- ::RiotJs::Rails::Compiler.compile(@data)
38
+ def self.register_self_helper(app, config, file_ext, mime_type_from, mime_type_to, charset=nil)
39
+
40
+ if config.respond_to?(:assets)
41
+ config.assets.configure do |env|
42
+ if env.respond_to?(:register_transformer)
43
+ # Sprockets 3 and 4
44
+ env.register_mime_type mime_type_from, extensions: [file_ext], charset: charset
45
+ env.register_transformer mime_type_from, mime_type_to, self
46
+ elsif env.respond_to?(:register_engine)
47
+ if Sprockets::VERSION.start_with?("3")
48
+ # Sprockets 3 ... is this needed?
49
+ env.register_engine file_ext, self, { mime_type: mime_type_to, silence_deprecation: true }
50
+ else
51
+ # Sprockets 2.12.4
52
+ @default_mime_type = mime_type_to
53
+ env.register_engine file_ext, self
54
+ end
55
+ end
56
+ end
57
+ else
58
+ # Sprockets 2.2.3
59
+ @default_mime_type = mime_type_to
60
+ app.assets.register_engine file_ext, self
61
+ end
62
+ end
63
+
64
+ end
65
+
66
+ class Processor < SprocketsExtensionBase
67
+
68
+ def self.run(filename, source, context)
69
+ ::RiotJs::Rails::Compiler.compile(source)
70
+ end
71
+
72
+ def self.register_self(app, config)
73
+ # app is a YourApp::Application
74
+ # config is Rails::Railtie::Configuration that belongs to RiotJs::Rails::Railtie
75
+ register_self_helper(app, config, '.tag', 'text/riot-tag', 'application/javascript', :html)
76
+ end
77
+
78
+ def self.register_nested(app, config, type, charset, tilt_template)
79
+ extention = '.' + type
80
+ if config.respond_to?(:assets)
81
+ config.assets.configure do |env|
82
+ if env.respond_to?(:register_transformer)
83
+ # Sprockets 3 and 4
84
+ env.register_mime_type 'text/riot-tag+'+type, extensions: ['.tag'+extention], charset: charset
85
+ env.register_transformer 'text/riot-tag+'+type, 'application/javascript',
86
+ Proc.new{ |input| Processor.call(tilt_template.new{input[:data]}.render) }
87
+ elsif env.respond_to?(:register_engine)
88
+ if Sprockets::VERSION.start_with?("3")
89
+ # Sprockets 3 ... is this needed?
90
+ env.register_engine extention, tilt_template, { silence_deprecation: true }
91
+ else
92
+ # Sprockets 2.12.4
93
+ env.register_engine extention, tilt_template
94
+ end
95
+ end
96
+ end
97
+ else
98
+ # Sprockets 2
99
+ app.assets.register_engine extention, tilt_template
100
+ end
21
101
  end
22
102
  end
23
103
  end
@@ -9,15 +9,18 @@ module RiotJs
9
9
  config.riot.node_paths = []
10
10
 
11
11
  initializer :setup_sprockets do |app|
12
- Processor.register_self config
12
+ # app is a YourApp::Application
13
+ # config is Rails::Railtie::Configuration that belongs to RiotJs::Rails::Railtie
14
+ Processor.register_self app, config
13
15
 
14
- if defined? ::Haml
16
+ if defined?(::Haml)
15
17
  require 'tilt/haml'
16
- config.assets.register_engine '.haml', ::Tilt::HamlTemplate
18
+ Haml::Template.options[:format] = :html5
19
+ Processor.register_nested(app, config, 'haml', :html, ::Tilt::HamlTemplate)
17
20
  end
18
21
 
19
- if defined? ::Slim
20
- config.assets.register_engine '.slim', ::Slim::Template
22
+ if defined?(::Slim)
23
+ Processor.register_nested(app, config, 'slim', :html, ::Slim::Template)
21
24
  end
22
25
  end
23
26
 
@@ -36,7 +39,6 @@ module RiotJs
36
39
  ENV['NODE_PATH'] = node_paths.join(':')
37
40
  end
38
41
 
39
-
40
42
  def detect_node_global_path
41
43
  prefix = `npm config get prefix`.to_s.chomp("\n")
42
44
  possible_paths = [ "#{prefix}/lib/node", "#{prefix}/lib/node_modules" ]
@@ -1,5 +1,5 @@
1
1
  module RiotJs
2
2
  module Rails
3
- VERSION = '0.6.2'
3
+ VERSION = '0.7.0'
4
4
  end
5
5
  end
@@ -1,7 +1,10 @@
1
+ 'use strict'
2
+
1
3
  /**
2
4
  * Brackets support for the node.js version of the riot-compiler
3
5
  * @module
4
6
  */
7
+ var safeRegex = require('./safe-regex.js')
5
8
 
6
9
  /**
7
10
  * Matches valid, multiline JavaScript comments in almost all its forms.
@@ -128,7 +131,7 @@ module.exports.split = function split (str, _, _bp) {
128
131
 
129
132
  isexpr = start = re.lastIndex = 0 // re is reused, we must reset lastIndex
130
133
 
131
- while (match = re.exec(str)) {
134
+ while ((match = re.exec(str))) {
132
135
 
133
136
  pos = match.index
134
137
 
@@ -202,7 +205,7 @@ module.exports.split = function split (str, _, _bp) {
202
205
 
203
206
  rr.lastIndex = ix
204
207
  ix = 1
205
- while (mm = rr.exec(s)) {
208
+ while ((mm = rr.exec(s))) {
206
209
  if (mm[1] &&
207
210
  !(mm[1] === ch ? ++ix : --ix)) break
208
211
  }
@@ -214,9 +217,8 @@ module.exports.split = function split (str, _, _bp) {
214
217
  }
215
218
  }
216
219
 
217
- var
218
- INVALIDCH = /[\x00-\x1F<>a-zA-Z0-9'",;\\]/, // invalid characters for brackets
219
- ESCAPEDCH = /(?=[[\]()*+?.^$|])/g // this characters must be escaped
220
+ var INVALIDCH = safeRegex(/[@-@<>a-zA-Z0-9'",;\\]/, 'x00', 'x1F') // invalid characters for brackets
221
+ var ESCAPEDCH = /(?=[[\]()*+?.^$|])/g // this characters must be escaped
220
222
 
221
223
  /**
222
224
  * Returns an array with information for the given brackets using a cache for the
@@ -4,13 +4,17 @@
4
4
  * @module compiler
5
5
  * @version WIP
6
6
  * @license MIT
7
- * @copyright 2015 Muut Inc. + contributors
7
+ * @copyright Muut Inc. + contributors
8
8
  */
9
9
  'use strict'
10
10
 
11
- var brackets = require('./brackets')
12
- var parsers = require('./parsers')
13
- var path = require('path')
11
+ var brackets = require('./brackets')
12
+ var parsers = require('./parsers')
13
+ var safeRegex = require('./safe-regex')
14
+ var path = require('path')
15
+
16
+ var extend = require('./parsers/_utils').mixobj
17
+ /* eslint-enable */
14
18
 
15
19
  /**
16
20
  * Source for creating regexes matching valid quoted, single-line JavaScript strings.
@@ -54,20 +58,14 @@ var HTML_COMMS = RegExp(/<!--(?!>)[\S\s]*?-->/.source + '|' + S_LINESTR, 'g')
54
58
  * {@link module:compiler~parseAttribs|parseAttribs}
55
59
  * @const {RegExp}
56
60
  */
57
- var HTML_TAGS = /<([-\w]+)(?:\s+([^"'\/>]*(?:(?:"[^"]*"|'[^']*'|\/[^>])[^'"\/>]*)*)|\s*)(\/?)>/g
61
+ var HTML_TAGS = /<(-?[A-Za-z][-\w\xA0-\xFF]*)(?:\s+([^"'\/>]*(?:(?:"[^"]*"|'[^']*'|\/[^>])[^'"\/>]*)*)|\s*)(\/?)>/g
58
62
 
59
63
  /**
60
- * Matches boolean HTML attributes, prefixed with `"__"` in the riot tag definition.
61
- * Used by {@link module:compiler~parseAttribs|parseAttribs} with lowercase names.
62
- * With a long list like this, a regex is faster than `[].indexOf` in most browsers.
63
- * @const {RegExp}
64
- * @see [attributes.md](https://github.com/riot/compiler/blob/dev/doc/attributes.md)
64
+ * Matches spaces and tabs between HTML tags
65
+ * Used by the `compact` option.
66
+ * @const RegExp
65
67
  */
66
- var BOOL_ATTRS = RegExp(
67
- '^(?:disabled|checked|readonly|required|allowfullscreen|auto(?:focus|play)|' +
68
- 'compact|controls|default|formnovalidate|hidden|ismap|itemscope|loop|' +
69
- 'multiple|muted|no(?:resize|shade|validate|wrap)?|open|reversed|seamless|' +
70
- 'selected|sortable|truespeed|typemustmatch)$')
68
+ var HTML_PACK = />[ \t]+<(-?[A-Za-z]|\/[-A-Za-z])/g
71
69
 
72
70
  /**
73
71
  * These attributes give error when parsed on browsers with an expression in its value.
@@ -76,7 +74,7 @@ var BOOL_ATTRS = RegExp(
76
74
  * @const {Array}
77
75
  * @see [attributes.md](https://github.com/riot/compiler/blob/dev/doc/attributes.md)
78
76
  */
79
- var RIOT_ATTRS = ['style', 'src', 'd']
77
+ var RIOT_ATTRS = ['style', 'src', 'd', 'value']
80
78
 
81
79
  /**
82
80
  * HTML5 void elements that cannot be auto-closed.
@@ -103,6 +101,12 @@ var PRE_TAGS = /<pre(?:\s+(?:[^">]*|"[^"]*")*)?>([\S\s]+?)<\/pre\s*>/gi
103
101
  */
104
102
  var SPEC_TYPES = /^"(?:number|date(?:time)?|time|month|email|color)\b/i
105
103
 
104
+ /**
105
+ * Matches the 'import' statement
106
+ * @const {RegExp}
107
+ */
108
+ var IMPORT_STATEMENT = /^\s*import(?:(?:\s|[^\s'"])*)['|"].*\n?/gm
109
+
106
110
  /**
107
111
  * Matches trailing spaces and tabs by line.
108
112
  * @const {RegExp}
@@ -110,6 +114,10 @@ var SPEC_TYPES = /^"(?:number|date(?:time)?|time|month|email|color)\b/i
110
114
  var TRIM_TRAIL = /[ \t]+$/gm
111
115
 
112
116
  var
117
+ RE_HASEXPR = safeRegex(/@#\d/, 'x01'),
118
+ RE_REPEXPR = safeRegex(/@#(\d+)/g, 'x01'),
119
+ CH_IDEXPR = '\x01#',
120
+ CH_DQCODE = '\u2057',
113
121
  DQ = '"',
114
122
  SQ = "'"
115
123
 
@@ -132,7 +140,7 @@ function cleanSource (src) {
132
140
  }
133
141
 
134
142
  re.lastIndex = 0
135
- while (mm = re.exec(src)) {
143
+ while ((mm = re.exec(src))) {
136
144
  if (mm[0][0] === '<') {
137
145
  src = RegExp.leftContext + RegExp.rightContext
138
146
  re.lastIndex = mm[3] + 1
@@ -160,7 +168,7 @@ function parseAttribs (str, pcex) {
160
168
 
161
169
  str = str.replace(/\s+/g, ' ')
162
170
 
163
- while (match = HTML_ATTRS.exec(str)) {
171
+ while ((match = HTML_ATTRS.exec(str))) {
164
172
  var
165
173
  k = match[1].toLowerCase(),
166
174
  v = match[2]
@@ -176,11 +184,10 @@ function parseAttribs (str, pcex) {
176
184
  if (k === 'type' && SPEC_TYPES.test(v)) {
177
185
  type = v
178
186
  } else {
179
- if (/\u0001\d/.test(v)) {
187
+ if (RE_HASEXPR.test(v)) {
180
188
 
181
189
  if (k === 'value') vexp = 1
182
- else if (BOOL_ATTRS.test(k)) k = '__' + k
183
- else if (~RIOT_ATTRS.indexOf(k)) k = 'riot-' + k
190
+ if (~RIOT_ATTRS.indexOf(k)) k = 'riot-' + k
184
191
  }
185
192
 
186
193
  list.push(k + '=' + v)
@@ -213,19 +220,17 @@ function splitHtml (html, opts, pcex) {
213
220
  var
214
221
  jsfn = opts.expr && (opts.parser || opts.type) ? _compileJS : 0,
215
222
  list = brackets.split(html, 0, _bp),
216
- expr, israw
223
+ expr
217
224
 
218
225
  for (var i = 1; i < list.length; i += 2) {
219
226
  expr = list[i]
220
227
  if (expr[0] === '^') {
221
228
  expr = expr.slice(1)
222
229
  } else if (jsfn) {
223
- israw = expr[0] === '='
224
- expr = jsfn(israw ? expr.slice(1) : expr, opts).trim()
230
+ expr = jsfn(expr, opts).trim()
225
231
  if (expr.slice(-1) === ';') expr = expr.slice(0, -1)
226
- if (israw) expr = '=' + expr
227
232
  }
228
- list[i] = '\u0001' + (pcex.push(expr) - 1) + _bp[1]
233
+ list[i] = CH_IDEXPR + (pcex.push(expr) - 1) + _bp[1]
229
234
  }
230
235
  html = list.join('')
231
236
  }
@@ -242,20 +247,10 @@ function splitHtml (html, opts, pcex) {
242
247
  */
243
248
  function restoreExpr (html, pcex) {
244
249
  if (pcex.length) {
245
- html = html
246
- .replace(/\u0001(\d+)/g, function (_, d) {
247
- var expr = pcex[d]
248
-
249
- if (expr[0] === '=') {
250
- expr = expr.replace(brackets.R_STRINGS, function (qs) {
251
- return qs
252
- .replace(/</g, '&lt;')
253
- .replace(/>/g, '&gt;')
254
- })
255
- }
250
+ html = html.replace(RE_REPEXPR, function (_, d) {
256
251
 
257
- return pcex._bp[0] + expr.trim().replace(/[\r\n]+/g, ' ').replace(/"/g, '\u2057')
258
- })
252
+ return pcex._bp[0] + pcex[d].trim().replace(/[\r\n]+/g, ' ').replace(/"/g, CH_DQCODE)
253
+ })
259
254
  }
260
255
  return html
261
256
  }
@@ -271,6 +266,7 @@ function restoreExpr (html, pcex) {
271
266
  * @see {@link http://www.w3.org/TR/html5/syntax.html}
272
267
  */
273
268
  function _compileHTML (html, opts, pcex) {
269
+ if (!/\S/.test(html)) return ''
274
270
 
275
271
  html = splitHtml(html, opts, pcex)
276
272
  .replace(HTML_TAGS, function (_, name, attr, ends) {
@@ -299,7 +295,7 @@ function _compileHTML (html, opts, pcex) {
299
295
  if (p.length) html = html.replace(/\u0002/g, function () { return p.shift() })
300
296
  }
301
297
 
302
- if (opts.compact) html = html.replace(/>[ \t]+<([-\w\/])/g, '><$1')
298
+ if (opts.compact) html = html.replace(HTML_PACK, '><$1')
303
299
 
304
300
  return restoreExpr(html, pcex).replace(TRIM_TRAIL, '')
305
301
  }
@@ -377,7 +373,7 @@ function riotjs (js) {
377
373
 
378
374
  if (~js.indexOf('/')) js = rmComms(js, JS_COMMS)
379
375
 
380
- while (match = js.match(JS_ES6SIGN)) {
376
+ while ((match = js.match(JS_ES6SIGN))) {
381
377
 
382
378
  parts.push(RE.leftContext)
383
379
  js = RE.rightContext
@@ -396,7 +392,7 @@ function riotjs (js) {
396
392
 
397
393
  function rmComms (s, r, m) {
398
394
  r.lastIndex = 0
399
- while (m = r.exec(s)) {
395
+ while ((m = r.exec(s))) {
400
396
  if (m[0][0] === '/' && !m[1] && !m[2]) {
401
397
  s = RE.leftContext + ' ' + RE.rightContext
402
398
  r.lastIndex = m[3] + 1
@@ -434,11 +430,8 @@ function _compileJS (js, opts, type, parserOpts, url) {
434
430
  if (!/\S/.test(js)) return ''
435
431
  if (!type) type = opts.type
436
432
 
437
- var parser = opts.parser || (type ? parsers.js[type] : riotjs)
433
+ var parser = opts.parser || type && parsers._req('js.' + type, true) || riotjs
438
434
 
439
- if (!parser) {
440
- throw new Error('JS parser not found: "' + type + '"')
441
- }
442
435
  return parser(js, parserOpts, url).replace(/\r\n?/g, '\n').replace(TRIM_TRAIL, '')
443
436
  }
444
437
 
@@ -478,7 +471,7 @@ function compileJS (js, opts, type, userOpts) {
478
471
  return _compileJS(js, opts || {}, type, userOpts.parserOptions, userOpts.url)
479
472
  }
480
473
 
481
- var CSS_SELECTOR = RegExp('([{}]|^)[ ;]*([^@ ;{}][^{}]*)(?={)|' + S_LINESTR, 'g')
474
+ var CSS_SELECTOR = RegExp('([{}]|^)[; ]*((?:[^@ ;{}][^{}]*)?[^@ ;{}:] ?)(?={)|' + S_LINESTR, 'g')
482
475
 
483
476
  /**
484
477
  * Parses styles enclosed in a "scoped" tag (`scoped` was removed from HTML5).
@@ -498,17 +491,21 @@ function scopedCSS (tag, css) {
498
491
  p2 = p2.replace(/[^,]+/g, function (sel) {
499
492
  var s = sel.trim()
500
493
 
494
+ if (s.indexOf(tag) === 0) {
495
+ return sel
496
+ }
497
+
501
498
  if (!s || s === 'from' || s === 'to' || s.slice(-1) === '%') {
502
499
  return sel
503
500
  }
504
501
 
505
502
  if (s.indexOf(scope) < 0) {
506
- s = tag + ' ' + s + ',[riot-tag="' + tag + '"] ' + s
503
+ s = tag + ' ' + s + ',[data-is="' + tag + '"] ' + s
507
504
  } else {
508
505
  s = s.replace(scope, tag) + ',' +
509
- s.replace(scope, '[riot-tag="' + tag + '"]')
506
+ s.replace(scope, '[data-is="' + tag + '"]')
510
507
  }
511
- return sel.slice(-1) === ' ' ? s + ' ' : s
508
+ return s
512
509
  })
513
510
 
514
511
  return p1 ? p1 + ' ' + p2 : p2
@@ -530,26 +527,19 @@ function scopedCSS (tag, css) {
530
527
  * @see {@link module:compiler.compileCSS|compileCSS}
531
528
  */
532
529
  function _compileCSS (css, tag, type, opts) {
533
- var scoped = (opts || (opts = {})).scoped
530
+ opts = opts || {}
534
531
 
535
532
  if (type) {
536
- if (type === 'scoped-css') {
537
- scoped = true
538
- } else if (parsers.css[type]) {
539
- css = parsers.css[type](tag, css, opts.parserOpts || {}, opts.url)
540
- } else if (type !== 'css') {
541
- throw new Error('CSS parser not found: "' + type + '"')
533
+ if (type !== 'css') {
534
+
535
+ var parser = parsers._req('css.' + type, true)
536
+ css = parser(tag, css, opts.parserOpts || {}, opts.url)
542
537
  }
543
538
  }
544
539
 
545
540
  css = css.replace(brackets.R_MLCOMMS, '').replace(/\s+/g, ' ').trim()
541
+ if (tag) css = scopedCSS(tag, css)
546
542
 
547
- if (scoped) {
548
- if (!tag) {
549
- throw new Error('Can not parse scoped CSS without a tagName')
550
- }
551
- css = scopedCSS(tag, css)
552
- }
553
543
  return css
554
544
  }
555
545
 
@@ -624,7 +614,7 @@ var MISC_ATTR = '\\s*=\\s*(' + S_STRINGS + '|{[^}]+}|\\S+)'
624
614
  * ```
625
615
  * @const {RegExp}
626
616
  */
627
- var END_TAGS = /\/>\n|^<(?:\/?[-\w]+\s*|[-\w]+\s+[-\w:\xA0-\xFF][\S\s]*?)>\n/
617
+ var END_TAGS = /\/>\n|^<(?:\/?-?[A-Za-z][-\w\xA0-\xFF]*\s*|-?[A-Za-z][-\w\xA0-\xFF]*\s+[-\w:\xA0-\xFF][\S\s]*?)>\n/
628
618
 
629
619
  /**
630
620
  * Encloses the given string in single quotes.
@@ -646,25 +636,26 @@ function _q (s, r) {
646
636
  /**
647
637
  * Generates code to call the `riot.tag2` function with the processed parts.
648
638
  *
649
- * @param {string} name - The tag name
650
- * @param {string} html - HTML (can contain embeded eols)
651
- * @param {string} css - Styles
652
- * @param {string} attribs - Root attributes
653
- * @param {string} js - JavaScript "constructor"
654
- * @param {Array} pcex - Expressions
639
+ * @param {string} name - The tag name
640
+ * @param {string} html - HTML (can contain embeded eols)
641
+ * @param {string} css - Styles
642
+ * @param {string} attr - Root attributes
643
+ * @param {string} js - JavaScript "constructor"
644
+ * @param {string} imports - Code containing 'import' statements
645
+ * @param {object} opts - Compiler options
655
646
  * @returns {string} Code to call `riot.tag2`
656
647
  */
657
- function mktag (name, html, css, attribs, js, pcex) {
648
+ function mktag (name, html, css, attr, js, imports, opts) {
658
649
  var
659
- c = ', ',
660
- s = '}' + (pcex.length ? ', ' + _q(pcex._bp[8]) : '') + ');'
650
+ c = opts.debug ? ',\n ' : ', ',
651
+ s = '});'
661
652
 
662
653
  if (js && js.slice(-1) !== '\n') s = '\n' + s
663
654
 
664
- return 'riot.tag2(\'' + name + SQ +
655
+ return imports + 'riot.tag2(\'' + name + SQ +
665
656
  c + _q(html, 1) +
666
657
  c + _q(css) +
667
- c + _q(attribs) + ', function(opts) {\n' + js + s
658
+ c + _q(attr) + ', function(opts) {\n' + js + s
668
659
  }
669
660
 
670
661
  /**
@@ -686,7 +677,9 @@ function splitBlocks (str) {
686
677
  m = str.slice(k, n).match(END_TAGS)
687
678
  if (m) {
688
679
  k += m.index + m[0].length
689
- return [str.slice(0, k), str.slice(k)]
680
+ m = str.slice(0, k)
681
+ if (m.slice(-5) === '<-/>\n') m = m.slice(0, -5)
682
+ return [m, str.slice(k)]
690
683
  }
691
684
  n = k
692
685
  k = str.lastIndexOf('<', k - 1)
@@ -732,6 +725,20 @@ function getAttrib (attribs, name) {
732
725
  return ''
733
726
  }
734
727
 
728
+ /**
729
+ * Unescape any html string
730
+ * @param {string} str escaped html string
731
+ * @returns {string} unescaped html string
732
+ */
733
+ function unescapeHTML (str) {
734
+ return str
735
+ .replace(/&amp;/g, '&')
736
+ .replace(/&lt;/g, '<')
737
+ .replace(/&gt;/g, '>')
738
+ .replace(/&quot;/g, '"')
739
+ .replace(/&#039;/g, '\'')
740
+ }
741
+
735
742
  /**
736
743
  * Gets the parser options from the "options" attribute.
737
744
  *
@@ -739,7 +746,7 @@ function getAttrib (attribs, name) {
739
746
  * @returns {object} Parsed options, or null if no options
740
747
  */
741
748
  function getParserOptions (attribs) {
742
- var opts = getAttrib(attribs, 'options')
749
+ var opts = unescapeHTML(getAttrib(attribs, 'options'))
743
750
 
744
751
  return opts ? JSON.parse(opts) : null
745
752
  }
@@ -756,9 +763,10 @@ function getParserOptions (attribs) {
756
763
  * @returns {string} Parsed code
757
764
  */
758
765
  function getCode (code, opts, attribs, base) {
759
- var type = getType(attribs)
760
-
761
- var src = getAttrib(attribs, 'src')
766
+ var
767
+ type = getType(attribs),
768
+ src = getAttrib(attribs, 'src'),
769
+ jsParserOptions = extend({}, opts.parserOptions.js)
762
770
 
763
771
  if (src) {
764
772
  if (DEFER_ATTR.test(attribs)) return false
@@ -768,7 +776,14 @@ function getCode (code, opts, attribs, base) {
768
776
 
769
777
  code = require('fs').readFileSync(file, charset || 'utf8')
770
778
  }
771
- return _compileJS(code, opts, type, getParserOptions(attribs), base)
779
+
780
+ return _compileJS(
781
+ code,
782
+ opts,
783
+ type,
784
+ extend(jsParserOptions, getParserOptions(attribs)),
785
+ base
786
+ )
772
787
  }
773
788
 
774
789
  /**
@@ -782,11 +797,12 @@ function getCode (code, opts, attribs, base) {
782
797
  * @returns {string} Parsed styles
783
798
  */
784
799
  function cssCode (code, opts, attribs, url, tag) {
785
- var extraOpts = {
786
- parserOpts: getParserOptions(attribs),
787
- scoped: attribs && /\sscoped(\s|=|$)/i.test(attribs),
788
- url: url
789
- }
800
+ var
801
+ parserStyleOptions = extend({}, opts.parserOptions.style),
802
+ extraOpts = {
803
+ parserOpts: extend(parserStyleOptions, getParserOptions(attribs)),
804
+ url: url
805
+ }
790
806
 
791
807
  return _compileCSS(code, tag, getType(attribs) || opts.style, extraOpts)
792
808
  }
@@ -803,11 +819,8 @@ function cssCode (code, opts, attribs, url, tag) {
803
819
  * @throws Will throw "Template parser not found" if the HTML parser cannot be loaded.
804
820
  */
805
821
  function compileTemplate (html, url, lang, opts) {
806
- var parser = parsers.html[lang]
807
822
 
808
- if (!parser) {
809
- throw new Error('Template parser not found: "' + lang + '"')
810
- }
823
+ var parser = parsers._req('html.' + lang, true)
811
824
  return parser(html, opts, url)
812
825
  }
813
826
 
@@ -820,7 +833,7 @@ var
820
833
  * unquoted expressions, but disallows the character '>' within unquoted attribute values.
821
834
  * @const {RegExp}
822
835
  */
823
- CUST_TAG = RegExp(/^([ \t]*)<([-\w]+)(?:\s+([^'"\/>]+(?:(?:@|\/[^>])[^'"\/>]*)*)|\s*)?(?:\/>|>[ \t]*\n?([\S\s]*)^\1<\/\2\s*>|>(.*)<\/\2\s*>)/
836
+ CUST_TAG = RegExp(/^([ \t]*)<(-?[A-Za-z][-\w\xA0-\xFF]*)(?:\s+([^'"\/>]+(?:(?:@|\/[^>])[^'"\/>]*)*)|\s*)?(?:\/>|>[ \t]*\n?([\S\s]*)^\1<\/\2\s*>|>(.*)<\/\2\s*>)/
824
837
  .source.replace('@', S_STRINGS), 'gim'),
825
838
  /**
826
839
  * Matches `script` elements, capturing its attributes in $1 and its content in $2.
@@ -865,10 +878,18 @@ var
865
878
  function compile (src, opts, url) {
866
879
  var
867
880
  parts = [],
868
- included
881
+ included,
882
+ defaultParserptions = {
883
+
884
+ template: {},
885
+ js: {},
886
+ style: {}
887
+ }
869
888
 
870
889
  if (!opts) opts = {}
871
890
 
891
+ opts.parserOptions = extend(defaultParserptions, opts.parserOptions || {})
892
+
872
893
  included = opts.exclude
873
894
  ? function (s) { return opts.exclude.indexOf(s) < 0 } : function () { return 1 }
874
895
 
@@ -877,7 +898,7 @@ function compile (src, opts, url) {
877
898
  var _bp = brackets.array(opts.brackets)
878
899
 
879
900
  if (opts.template) {
880
- src = compileTemplate(src, url, opts.template, opts.templateOptions)
901
+ src = compileTemplate(src, url, opts.template, opts.parserOptions.template)
881
902
  }
882
903
 
883
904
  src = cleanSource(src)
@@ -886,6 +907,7 @@ function compile (src, opts, url) {
886
907
  jscode = '',
887
908
  styles = '',
888
909
  html = '',
910
+ imports = '',
889
911
  pcex = []
890
912
 
891
913
  pcex._bp = _bp
@@ -908,13 +930,6 @@ function compile (src, opts, url) {
908
930
 
909
931
  body = body.replace(RegExp('^' + indent, 'gm'), '')
910
932
 
911
- body = body.replace(STYLES, function (_m, _attrs, _style) {
912
- if (included('css')) {
913
- styles += (styles ? ' ' : '') + cssCode(_style, opts, _attrs, url, tagName)
914
- }
915
- return ''
916
- })
917
-
918
933
  body = body.replace(SCRIPTS, function (_m, _attrs, _script) {
919
934
  if (included('js')) {
920
935
  var code = getCode(_script, opts, _attrs, url)
@@ -925,6 +940,13 @@ function compile (src, opts, url) {
925
940
  return ''
926
941
  })
927
942
 
943
+ body = body.replace(STYLES, function (_m, _attrs, _style) {
944
+ if (included('css')) {
945
+ styles += (styles ? ' ' : '') + cssCode(_style, opts, _attrs, url, tagName)
946
+ }
947
+ return ''
948
+ })
949
+
928
950
  var blocks = splitBlocks(body.replace(TRIM_TRAIL, ''))
929
951
 
930
952
  if (included('html')) {
@@ -934,6 +956,10 @@ function compile (src, opts, url) {
934
956
  if (included('js')) {
935
957
  body = _compileJS(blocks[1], opts, null, null, url)
936
958
  if (body) jscode += (jscode ? '\n' : '') + body
959
+ jscode = jscode.replace(IMPORT_STATEMENT, function (s) {
960
+ imports += s.trim() + '\n'
961
+ return ''
962
+ })
937
963
  }
938
964
  }
939
965
  }
@@ -946,12 +972,13 @@ function compile (src, opts, url) {
946
972
  html: html,
947
973
  css: styles,
948
974
  attribs: attribs,
949
- js: jscode
975
+ js: jscode,
976
+ imports: imports
950
977
  })
951
978
  return ''
952
979
  }
953
980
 
954
- return mktag(tagName, html, styles, attribs, jscode, pcex)
981
+ return mktag(tagName, html, styles, attribs, jscode, imports, opts)
955
982
  })
956
983
 
957
984
  if (opts.entities) return parts