slinky 0.7.3 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,15 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 33d6587d8e7257920874cf090c6591d9c0ffbbe4
4
- data.tar.gz: 1d67f8ebdbe3c530c33c37dd6fc5c206613b9f8a
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NTAyMTk5MzgwNzNkMTA0MzhhZjc0ODBmNjFiM2JlMTM4NDE2ZjNhYw==
5
+ data.tar.gz: !binary |-
6
+ YjdjMDMwZGIzZWU4YjE2MmIwOGU0ZTIyMzc2ZDYyZDg2M2I2NWRmMg==
5
7
  SHA512:
6
- metadata.gz: f6c8154b45b0259adaf9738f35e395cea591f900679020da3399b8c0d2c20f3c605ae3f77712dcb399ee0d54e14d6f40f8092e1527f252dc5f830fffd1c69af3
7
- data.tar.gz: e1d86f830c2af11375a62ebd8bf3c1b47a5109bbe704113508d70e3253fbeda1507990821f0e168a645704aa50c196f6c0cfddb044ee9e2939e95aba586c9b7d
8
+ metadata.gz: !binary |-
9
+ Nzc5Y2M4YjUzNGI1MDMyNTgxMzQwY2FkNWRkMDkwZGZjYWI3ODNmOWI0Mzk1
10
+ Nzk1ZjNlMzcxMDVjYjFiNWFhODNhZGUyMzcyYzM1YzBkMjI3MzljNGFhYzEw
11
+ ODFiNGFjZmM5MDYyNzAyMzc4ZjExNjQ3NDkyOWNmNzAxZTYyYzU=
12
+ data.tar.gz: !binary |-
13
+ YzQ1ZmIzMDY5NjdlYjMyYzg4MzZkMjczMzk5MGRlNjBiZGEyNWM1YmNhMzFj
14
+ Nzc1ZTQ1Yzc3OTdmZTk2YTc1NmE4ZDI3YjM3YTRkYWE0YzA2MmU1OThiODMy
15
+ Y2NjZDExZDY1ZGQ0YTg0NDBmMTg0MGFmNTM3ZjMwMzgzN2M5NzI=
data/.travis.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.1.0
3
+ - 2.1
4
4
  - 2.0.0
5
5
  - 1.9.3
data/Gemfile CHANGED
@@ -1,6 +1,7 @@
1
1
  source "http://rubygems.org"
2
2
 
3
3
  # main gems
4
+ gem 'multi_json', '~> 1.9.2'
4
5
  gem "eventmachine", "~> 1.0"
5
6
  gem "eventmachine_httpserver", "~> 0.2"
6
7
  gem "em-websocket", "~> 0.3"
@@ -26,4 +27,5 @@ group :development do
26
27
  # optional compilation gems
27
28
  gem "less", ">= 2.2.0"
28
29
  gem "therubyracer" # for less
30
+ gem "react-jsx", '~> 0.8.0'
29
31
  end
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Slinky
1
+ # Slinky
2
2
 
3
3
  If you write single-page rich client apps, Slinky is here to
4
4
  make your life easier. For development, it provides a static file
@@ -52,6 +52,8 @@ $ scp -r ../pub/ myserver.com:/var/www/project
52
52
  7. [PushState](#pushstate)
53
53
  8. [Proxies](#proxies)
54
54
  9. [Ignores](#ignores)
55
+ 10. [Products](#products)
56
+ 11. [Path matching](#path-matching)
55
57
 
56
58
  ### Transparent compilation
57
59
 
@@ -69,6 +71,7 @@ Currently supported languages include:
69
71
  * HAML
70
72
  * SASS/SCSS
71
73
  * LESS
74
+ * JSX (react templates)
72
75
  * ClojureScript (experimental)
73
76
 
74
77
  Adding support for new languages is simple, and pull requests are welcome.
@@ -109,7 +112,8 @@ sees this:
109
112
  it will compile the HAML to HTML and replace slinky_styles with the
110
113
  appropriate HTML. You can also disable minification with the
111
114
  `--dont-minify` option or the `dont_minify: true` configuration
112
- option.
115
+ option. `slinky_scripts` and `slinky_styles` are conveniences built on
116
+ top of the [full product system](#products).
113
117
 
114
118
  ### Specifying order
115
119
 
@@ -153,8 +157,8 @@ changes, you would like the HAML file to be recompiled so that the
153
157
  templates will also be updated.
154
158
 
155
159
  These relationships are specified as "dependencies," and like requirements
156
- they are incdicated through a special `slinky_depends("file")` directive in
157
- your source files. For our template example, the index.haml files might look
160
+ they are incdicated through a special `slinky_depends("file")` directive in
161
+ your source files. For our template example, the index.haml files might look
158
162
  like this:
159
163
 
160
164
  ```haml
@@ -188,9 +192,16 @@ pushstate:
188
192
  proxy:
189
193
  "/test1": "http://127.0.0.1:8000"
190
194
  "/test2": "http://127.0.0.1:7000"
191
- ignore:
192
- - script/vendor
193
- - script/jquery.js
195
+ produce:
196
+ "/scripts.js":
197
+ include:
198
+ - "*.js"
199
+ exclude:
200
+ - "/script/vendor/"
201
+ - "/script/jquery.js"
202
+ "/styles.css":
203
+ include:
204
+ - "*.css"
194
205
  port: 5555
195
206
  src_dir: "src/"
196
207
  build_dir: "build/"
@@ -198,6 +209,8 @@ no_proxy: true
198
209
  no_livereload: true
199
210
  livereload_port: 5556
200
211
  dont_minify: true
212
+ # enable browser error injection (experimental)
213
+ enable_browser_errors: true
201
214
  ```
202
215
 
203
216
  Most are self explanatory, but a few of the options merit further
@@ -212,7 +225,7 @@ that retain the advantages of their multi-page peers without resorting
212
225
  to hacks like hash urls. The essential idea is this: when a user
213
226
  navigates to a conceptually different "page" in the app, the URL
214
227
  should be updated to reflect that so that behaviors such as
215
- deep-linking and history navigation work properly.
228
+ deep-linking and history navigation work properly.
216
229
 
217
230
  For this to work, however, the server must be able to return the
218
231
  content of your main HTML page for arbitrary paths, as otherwise when
@@ -271,6 +284,9 @@ request and finally returns the response back to the browser.
271
284
 
272
285
  ### Ignores
273
286
 
287
+ _Ignores are deprecated and will be removed in the next major release.
288
+ Use the new product system instead._
289
+
274
290
  By default slinky will include every javascript and css file it finds
275
291
  into the combined scripts.js and styles.css files. However, it may be
276
292
  that for some reason you want to keep some files separate and handle
@@ -285,3 +301,98 @@ ignore:
285
301
 
286
302
  This will causes everything in the script/vendor directory to be
287
303
  ignored by slinky, as well as the reset.css file.
304
+
305
+ ### Products
306
+
307
+ _New in 0.8: use master to get them now_
308
+
309
+ Products are the outputs of the build system. Most files are just
310
+ copied to the build directory, but you may want some to undergo
311
+ further processing. For simplicity, Slinky defines two default
312
+ products which you have seen above: `/scripts.js` and `/styles.css`.
313
+ These are defined like this:
314
+
315
+ ```yaml
316
+ produce:
317
+ "/scripts.js":
318
+ include:
319
+ - "*.js"
320
+ "/styles.css":
321
+ include:
322
+ - "*.css"
323
+ ```
324
+
325
+ Products are defined by an output path (in this case `/scripts.js` and
326
+ `/styles.css`), a set of paths to include, and a set of paths to
327
+ exclude (with gitignore-style glob patterns supported; see
328
+ [here](#path-matching) for the match rules). In development mode, all
329
+ of the files included in a product will be included in your html
330
+ separately. When built in production mode, they will all be minified
331
+ and concatenated into a single output file. We can also create our own
332
+ products:
333
+
334
+ ```yaml
335
+ produce:
336
+ "/test/test.js":
337
+ include:
338
+ - "*_test.js"
339
+ "/main.js":
340
+ include:
341
+ - "*.js"
342
+ exclude:
343
+ - "vendor/jquery*.js"
344
+ - "*_test.js"
345
+ "/main.css":
346
+ include:
347
+ - "*.css"
348
+ exclude:
349
+ - "vendor/boostrap.css"
350
+ ```
351
+
352
+ This config will produce three products in the build directory:
353
+ `test/test.js`, which will include all files ending in `_test.js`,
354
+ `main.js` which includes all .js files except jquery and test files,
355
+ and `main.css` which includes all css files except for boostrap.css in
356
+ the vendor directory. Custom products can be included in your HTML
357
+ like this:
358
+
359
+ ```html
360
+ <html>
361
+ <head>
362
+ slinky_product("/main.js")
363
+ slinky_product("/main.css")
364
+ </head>
365
+ ...
366
+ ```
367
+
368
+ The default product directives (`slinky_scripts` and `slinky_styles`)
369
+ are merely sugar for `slinky_product("/scripts.js")` and
370
+ `slinky_product("/styles.css")`.
371
+
372
+ # Path matching
373
+
374
+ Several slinky config features involve specifying paths, with support
375
+ for globbing. These are interpreted similarly to .gitignore rules. The full
376
+ specification is:
377
+
378
+ 1. If the pattern ends with a slash, it will only match directories;
379
+ e.g. `foo/` would match a directory `foo/` but not a file `foo`. In
380
+ a file context, matching a directory is equivalent to matching all
381
+ files under that directory, recursively.
382
+ 2. If the pattern does not contain a slash, slinky treats it as a
383
+ relative pathname which can match files in any directory. For
384
+ example, the rule `test.js` will matching `/test.js` and
385
+ `/component/test.js`.
386
+ 3. If the pattern begins with a slash, it will be treated as an
387
+ absolute path starting at the root of the source directory.
388
+ 4. If the pattern does not begin with a slash, but does contain one or
389
+ more slashes, it will be treated as a path relative to any
390
+ directory. For example, `test/*.js` will match `/test/main.js`, and
391
+ `/component/test/component.js`, but not `main.js`.
392
+ 5. A single star `*` in a pattern will match any number of characters within a
393
+ single path component. For example, `/test/*.js` will match
394
+ `/test/main_test.js` but not `/test/component/test.js`.
395
+ 6. A double star `**` will match any number of characters including
396
+ path separators. For example `/scripts/**/main.js` will match any
397
+ file named `main.js` under the `/scripts` directory, including
398
+ `/scripts/main.js` and `/scripts/component/main.js`.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.7.3
1
+ 0.8.0
data/lib/slinky.rb CHANGED
@@ -14,8 +14,10 @@ require 'listen'
14
14
  require 'multi_json'
15
15
 
16
16
  require "slinky/em-popen3"
17
+ require "slinky/errors"
17
18
  require "slinky/compilers"
18
19
  require "slinky/config_reader"
20
+ require "slinky/graph"
19
21
  require "slinky/manifest"
20
22
  require "slinky/compiled_file"
21
23
  require "slinky/proxy_server"
@@ -9,8 +9,10 @@ module Slinky
9
9
  :no_minify => config.dont_minify || options[:no_minify])
10
10
  begin
11
11
  manifest.build
12
- rescue BuildFailedError
13
- $stderr.puts "Build failed"
12
+ rescue SlinkyError => e
13
+ e.messages.each{|m|
14
+ $stderr.puts(m.foreground(:red))
15
+ }
14
16
  end
15
17
  end
16
18
  end
@@ -74,7 +74,7 @@ module Slinky
74
74
  end
75
75
 
76
76
  def compile_failed e
77
- $stderr.puts "Failed on #{name}: #{e}".foreground(:red)
77
+ SlinkyError.raise BuildFailedError, "Compilation failed on #{name}: #{e}"
78
78
  end
79
79
 
80
80
  # Calls the supplied callback with the path of the compiled file,
@@ -34,13 +34,12 @@ module Slinky
34
34
  end
35
35
 
36
36
  def has_dependencies compiler, ext
37
- (compiler[:dependencies] || []).all? {|d|
37
+ ds = (compiler[:dependencies] || []).all? {|d|
38
38
  if @checked_dependencies.include?(d)
39
39
  true
40
40
  else
41
41
  begin
42
42
  gem(d[0], d[1])
43
- require d[0]
44
43
  @checked_dependencies.add(d)
45
44
  true
46
45
  rescue Gem::LoadError
@@ -49,6 +48,10 @@ module Slinky
49
48
  end
50
49
  end
51
50
  }
51
+ (compiler[:requires] || []).each {|d|
52
+ require d
53
+ }
54
+ ds
52
55
  end
53
56
 
54
57
  # Produces a CompiledFile for an input file if the file needs to
@@ -1,10 +1,10 @@
1
1
  module Slinky
2
2
  module ClojureScriptCompiler
3
3
  Compilers.register_compiler self,
4
- :inputs => ["cljs"],
5
- :outputs => ["js"],
6
- :dependencies => [["clementine", "~> 0.0.3"]]
7
-
4
+ :inputs => ["cljs"],
5
+ :outputs => ["js"],
6
+ :dependencies => [["clementine", "~> 0.0.3"]],
7
+ :requires => ["clementine"]
8
8
  def ClojureScriptCompiler::compile s, file
9
9
  # Clementine.options[:pretty_print] = true
10
10
  # Clementine.options[:optimizations] = :none
@@ -1,9 +1,10 @@
1
1
  module Slinky
2
2
  module CoffeeCompiler
3
3
  Compilers.register_compiler self,
4
- :inputs => ["coffee"],
5
- :outputs => ["js"],
6
- :dependencies => [["coffee-script", ">= 2.2.0"]]
4
+ :inputs => ["coffee"],
5
+ :outputs => ["js"],
6
+ :dependencies => [["coffee-script", ">= 2.2.0"]],
7
+ :requires => ["coffee-script"]
7
8
 
8
9
  def CoffeeCompiler::compile s, file
9
10
  CoffeeScript::compile(s)
@@ -1,9 +1,10 @@
1
1
  module Slinky
2
2
  module HamlCompiler
3
3
  Compilers.register_compiler self,
4
- :inputs => ["haml"],
5
- :outputs => ["html"],
6
- :dependencies => [["haml", "~> 3.1.0"]]
4
+ :inputs => ["haml"],
5
+ :outputs => ["html"],
6
+ :dependencies => [["haml", "~> 3.1.0"]],
7
+ :requires => ["haml"]
7
8
 
8
9
  def HamlCompiler::compile s, file
9
10
  haml_engine = Haml::Engine.new(s)
@@ -0,0 +1,13 @@
1
+ module Slinky
2
+ module JSXCompiler
3
+ Compilers.register_compiler self,
4
+ :inputs => ["jsx"],
5
+ :outputs => ["js"],
6
+ :dependencies => [["react-jsx", "~> 0.8.0"]],
7
+ :requires => ["react/jsx"]
8
+
9
+ def JSXCompiler::compile s, file
10
+ React::JSX.compile(s)
11
+ end
12
+ end
13
+ end
@@ -1,9 +1,10 @@
1
1
  module Slinky
2
2
  module LessCompiler
3
3
  Compilers.register_compiler self,
4
- :inputs => ["less"],
5
- :outputs => ["css"],
6
- :dependencies => [["less", ">= 2.2.0"]]
4
+ :inputs => ["less"],
5
+ :outputs => ["css"],
6
+ :dependencies => [["less", ">= 2.2.0"]],
7
+ :requires => ["less"]
7
8
 
8
9
  def LessCompiler::compile s, file
9
10
  parser = Less::Parser.new
@@ -1,9 +1,10 @@
1
1
  module Slinky
2
2
  module SassCompiler
3
3
  Compilers.register_compiler self,
4
- :inputs => ["sass", "scss"],
5
- :outputs => ["css"],
6
- :dependencies => [["sass", ">= 3.1.1"]]
4
+ :inputs => ["sass", "scss"],
5
+ :outputs => ["css"],
6
+ :dependencies => [["sass", ">= 3.1.1"]],
7
+ :requires => ["sass"]
7
8
 
8
9
  def SassCompiler::compile s, file
9
10
  syntax = file.end_with?(".sass") ? :sass : :scss
@@ -10,6 +10,9 @@ module Slinky
10
10
  BOOL_TYPE = "bool"
11
11
  ANY_TYPE = "any"
12
12
 
13
+ DEFAULT_SCRIPT_PRODUCT = "/scripts.js"
14
+ DEFAULT_STYLE_PRODUCT = "/styles.css"
15
+
13
16
  class ConfigEntry
14
17
  attr_reader :type, :name, :default
15
18
  def initialize(name, type, default)
@@ -30,6 +33,12 @@ module Slinky
30
33
  ConfigEntry.new("livereload_port", NUMBER_TYPE, 35729),
31
34
  ConfigEntry.new("dont_minify", BOOL_TYPE, false),
32
35
  ConfigEntry.new("pushstate", ANY_TYPE, []),
36
+ ConfigEntry.new("dependencies", HASH_TYPE, {}),
37
+ ConfigEntry.new("produce", HASH_TYPE, {
38
+ DEFAULT_SCRIPT_PRODUCT => {"include" => ["*.js"]},
39
+ DEFAULT_STYLE_PRODUCT => {"include" => ["*.css"]}
40
+ }),
41
+ ConfigEntry.new("enable_browser_errors", BOOL_TYPE, false)
33
42
  ]
34
43
 
35
44
  @entries.each{|e|
@@ -64,8 +73,15 @@ module Slinky
64
73
  end
65
74
  end
66
75
 
67
- def initialize string
68
- @config = YAML::load(string)
76
+ def initialize string_or_hash
77
+ case string_or_hash
78
+ when String
79
+ @config = YAML::load(string_or_hash)
80
+ when Hash
81
+ @config = string_or_hash
82
+ else
83
+ raise TypeError.new("Config must be either a string or a hash")
84
+ end
69
85
  ConfigReader.validate(@config)
70
86
  end
71
87
 
@@ -0,0 +1,91 @@
1
+ require 'continuation'
2
+
3
+ module Slinky
4
+ # Common base class for all Slinky errors
5
+ class SlinkyError < StandardError
6
+ class NoContinuationError < StandardError; end
7
+
8
+ attr_accessor :continuation
9
+
10
+ # Continue where we left off
11
+ def continue
12
+ raise NoContinuationError unless continuation.respond_to?(:call)
13
+ continuation.call
14
+ end
15
+
16
+ def messages
17
+ [message]
18
+ end
19
+
20
+ # Raises an error with a continuation that allows us to continue
21
+ # with processing as if no error had been thrown
22
+ def self.raise(exception = SlinkyError, string = nil, array = caller)
23
+ if exception.is_a?(String)
24
+ string = exception
25
+ exception = SlinkyError
26
+ end
27
+
28
+ callcc do |cc|
29
+ obj = string.nil? ? exception : exception.exception(string)
30
+ obj.set_backtrace(array)
31
+ obj.continuation = cc
32
+ super obj
33
+ end
34
+ end
35
+
36
+ # batches all SlinkyErrors thrown in the supplied block and
37
+ # re-raises them at the end of processing wrapped in a MultiError.
38
+ def self.batch_errors
39
+ errors = []
40
+ result = nil
41
+ begin
42
+ result = yield
43
+ rescue SlinkyError => e
44
+ errors << e
45
+ if e.continuation.respond_to?(:call)
46
+ e.continue
47
+ end
48
+ end
49
+
50
+ if !errors.empty?
51
+ if errors.size == 1
52
+ raise errors.first
53
+ else
54
+ raise MultiError, errors
55
+ end
56
+ end
57
+ result
58
+ end
59
+ end
60
+
61
+ # Raised when a compilation fails for any reason
62
+ class BuildFailedError < SlinkyError; end
63
+ # Raised when a required file is not found.
64
+ class FileNotFoundError < SlinkyError; end
65
+ # Raised when there is a cycle in the dependency graph (i.e., file A
66
+ # requires file B which requires C which requires A)
67
+ class DependencyError < SlinkyError; end
68
+ # Raise when there is a request for a product that doesn't exist
69
+ class NoSuchProductError < SlinkyError; end
70
+ # Raise when there are multiple errors
71
+ class MultiError < SlinkyError
72
+ attr_reader :errors
73
+
74
+ def initialize errors
75
+ @errors = errors.map{|e|
76
+ case e
77
+ when MultiError then e.errors
78
+ else e
79
+ end
80
+ }.flatten
81
+ end
82
+
83
+ def message
84
+ @errors.map{|e| e.message}.join(", ")
85
+ end
86
+
87
+ def messages
88
+ @errors.map{|e| e.message}
89
+ end
90
+ end
91
+ end