assets-squasher 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +65 -0
  3. data/bin/assets-squasher +137 -0
  4. metadata +62 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 793d907e712e886eeea8903b6beb59febaadf644
4
+ data.tar.gz: e6cb4c394b955dcda52810fc2f7b64ee38e75290
5
+ SHA512:
6
+ metadata.gz: d48bcf5fc377ec8e98028c5923305b41201482e675477c6998ec027ed80bff971eea1dd0d8d0ec195d7d63e51d5ee5f9cd5944fe2cf8c0f2ec90497913e5052b
7
+ data.tar.gz: b5b9e742aec83140022599b5272fbc640370afb3e7237066ca3899bf5479b3a95259153fc6c7fb5b23d443ea625a0c73f60e5cd1e33a7486430753b64ab138ce
@@ -0,0 +1,65 @@
1
+ # About
2
+
3
+ Assets squasher is exactly what it says on the tin: it takes an HTML file and replaces all included scripts with just one and the same for stylesheets.
4
+
5
+ One caveat here, it's meant to be used for **single page applications**. There you typically have one entry point HTML file which links all the necessary scripts and stylesheets. Like this one:
6
+
7
+ ```html
8
+ <!DOCTYPE html>
9
+
10
+ <html ng-app="app" ng-controller="MainController">
11
+ <head>
12
+ <script defer src="/bower_components/angular/angular.min.js"></script>
13
+ <script defer src="/bower_components/angular-route/angular-route.min.js"></script>
14
+ <script defer src="/bower_components/angular-animate/angular-animate.min.js"></script>
15
+ <script defer src="/bower_components/angular-ui-bootstrap/dist/ui-bootstrap-tpls-0.11.0.min.js"></script>
16
+ <script defer src="/app.js"></script>
17
+ <script defer src="/services.js"></script>
18
+
19
+ <link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css">
20
+ <link rel="stylesheet" href="/bower_components/font-awesome/css/font-awesome.min.css">
21
+ <link rel="stylesheet" href="/app.css">
22
+ </head>
23
+
24
+ <body>
25
+ <div ng-view>Loading ...</div>
26
+ </body>
27
+ </html>
28
+ ```
29
+
30
+ Now let's save it into `app.html` (I'm assuming that all the assets exist). When you run `bundle exec assets-asquasher app.html build.html`, you'll get `build/build.min.js` and its source map, `build/build.min.css` and also `build.html` which will look like this:
31
+
32
+ ```html
33
+ <!DOCTYPE html>
34
+
35
+ <html ng-app="app" ng-controller="MainController">
36
+ <head>
37
+ <script src="/build/build.min.js"></script>
38
+ <link rel="stylesheet" href="/build/build.min.css">
39
+ </head>
40
+
41
+ <body>
42
+ <div ng-view>Loading ...</div>
43
+ </body>
44
+ </html>
45
+ ```
46
+
47
+ ## Why Is It Useful?
48
+
49
+ On production you want to use a single minified JS file and a single minified CSS file. Why? Because speed has significant impact on conversions. Yes, it's all cached after the first request, but how many times have you closed a web page before it even loaded?
50
+
51
+ On the other hand in development you want to include each unminified script separately, so the backtraces make sense[1].
52
+
53
+ Ultimately this the simplest solution: no need to change your code like you'd have to with say RequireJS. No need to define dependencies in an extra file – it's already there!
54
+
55
+ ## Installation
56
+
57
+ ```
58
+ # Assuming you have Node.js already installed.
59
+ npm -g uglify-js uglifycss
60
+ gem install assets-asquasher
61
+ ```
62
+
63
+ ## Footnotes
64
+
65
+ [1] Or alternatively you could use one build file with source maps, but then you have to user guard or something to constantly rebuild it.
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'nokogiri'
4
+
5
+ html_file = ARGV.first
6
+
7
+ # Validate arguments.
8
+ unless ARGV.length == 2 && File.exist?(html_file)
9
+ abort <<-EOF
10
+ Usage: #{$0} [input HTML file path] [output HTML file path]
11
+
12
+ Those can be the same if you want to override the original file.
13
+ That can be useful for deployment, so you keep the original file
14
+ in your project, using it for development while for production
15
+ it is overriden for faster load times.
16
+ EOF
17
+ end
18
+
19
+ document = Nokogiri::HTML(File.read(html_file))
20
+
21
+ # Change CWD.
22
+ Dir.chdir(File.dirname(html_file))
23
+
24
+ puts "~ Assuming #{Dir.pwd} as the root directory."
25
+
26
+ # Helper methods.
27
+ def list(document, selector, attribute)
28
+ values = document.css(selector).map do |element|
29
+ value = element.attr(attribute)
30
+ value.start_with?('/') ? value[1..-1] : value
31
+ end
32
+
33
+ document.search(selector).remove
34
+
35
+ values
36
+ end
37
+
38
+ def run(command)
39
+ puts "~ $ #{command}"
40
+ %x{#{command}}.split("\n").each do |line|
41
+ puts " #{line}"
42
+ end; puts
43
+ end
44
+
45
+ # Main.
46
+ scripts = list(document, 'html > head > script[src]', 'src')
47
+ stylesheets = list(document, 'html > head > link[rel="stylesheet"]', 'href')
48
+
49
+ Dir.mkdir('build') unless Dir.exist?('build')
50
+
51
+ # Uglify
52
+ run "uglifyjs -o build/build.min.js --source-map build/build.min.js.map #{scripts.join(' ')}"
53
+ run "uglifycss #{stylesheets.join(' ')} > build/build.min.css"
54
+
55
+ script = document.create_element('script', src: '/build/build.min.js', defer: true)
56
+ document.at_css('head').children.after(script)
57
+
58
+ stylesheet = document.create_element('link', rel: 'stylesheet', href: '/build/build.min.css')
59
+ document.at_css('head').children.after(stylesheet)
60
+
61
+
62
+ # reformatter_path = File.expand_path('../../assets/reformat-html.xsl', __FILE__)
63
+ # reformatter_path = "/webs/ppt/assets-squasher/assets/reformat-html.xsl"
64
+
65
+ xsl = Nokogiri::XSLT(DATA)
66
+ lines = xsl.apply_to(document).split("\n")
67
+ lines[0] = "<!DOCTYPE html>"
68
+
69
+ File.open(ARGV[1], 'w') do |file|
70
+ file.puts(lines.join("\n"))
71
+ end
72
+
73
+ __END__
74
+ <!-- http://emmanueloga.wordpress.com/2009/09/29/pretty-printing-xhtml-with-nokogiri-and-xslt/ -->
75
+ <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
76
+ <!-- It slightly missindents with method="html", at least on the file I tried. -->
77
+ <xsl:output method="xml" encoding="utf-8"/>
78
+
79
+ <!--
80
+ Producting HTML 5 doctype is a PITA, as per
81
+ http://stackoverflow.com/questions/3387127/set-html5-doctype-with-xslt
82
+
83
+ Let's just do it from Ruby.
84
+ -->
85
+ <!-- <xsl:output method="html" encoding="utf-8" indent="yes" /> -->
86
+ <!-- <xsl:output method="html" version="5.0" encoding="UTF-8" indent="yes" /> -->
87
+
88
+ <!-- <xsl:text disable-output-escaping="yes">&lt;!DOCTYPE html&gt;</xsl:text> -->
89
+
90
+ <!-- <xsl:template match="/">
91
+ <xsl:text disable-output-escaping='yes'>&lt;!DOCTYPE html></xsl:text>
92
+ </xsl:template> -->
93
+
94
+ <xsl:param name="indent-increment" select="' '"/>
95
+
96
+ <xsl:template name="newline">
97
+ <xsl:text disable-output-escaping="yes">
98
+ </xsl:text>
99
+ </xsl:template>
100
+
101
+ <xsl:template match="comment() | processing-instruction()">
102
+ <xsl:param name="indent" select="''"/>
103
+ <xsl:call-template name="newline"/>
104
+ <xsl:value-of select="$indent"/>
105
+ <xsl:copy />
106
+ </xsl:template>
107
+
108
+ <xsl:template match="text()">
109
+ <xsl:param name="indent" select="''"/>
110
+ <xsl:call-template name="newline"/>
111
+ <xsl:value-of select="$indent"/>
112
+ <xsl:value-of select="normalize-space(.)"/>
113
+ </xsl:template>
114
+
115
+ <xsl:template match="text()[normalize-space(.)='']"/>
116
+
117
+ <xsl:template match="*">
118
+ <xsl:param name="indent" select="''"/>
119
+ <xsl:call-template name="newline"/>
120
+ <xsl:value-of select="$indent"/>
121
+ <xsl:choose>
122
+ <xsl:when test="count(child::*) > 0">
123
+ <xsl:copy>
124
+ <xsl:copy-of select="@*"/>
125
+ <xsl:apply-templates select="*|text()">
126
+ <xsl:with-param name="indent" select="concat ($indent, $indent-increment)"/>
127
+ </xsl:apply-templates>
128
+ <xsl:call-template name="newline"/>
129
+ <xsl:value-of select="$indent"/>
130
+ </xsl:copy>
131
+ </xsl:when>
132
+ <xsl:otherwise>
133
+ <xsl:copy-of select="."/>
134
+ </xsl:otherwise>
135
+ </xsl:choose>
136
+ </xsl:template>
137
+ </xsl:stylesheet>
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: assets-squasher
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - https://github.com/botanicus
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-06-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: nokogiri
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: It takes an HTML file, traverses over its scripts and stylesheets and
28
+ minify them all into one file using UglifyJS2 and UglifyCSS. Meant primarily for
29
+ single page apps.
30
+ email: james@101ideas.cz
31
+ executables:
32
+ - assets-squasher
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - README.md
37
+ - bin/assets-squasher
38
+ homepage: https://github.com/botanicus/assets-squasher
39
+ licenses:
40
+ - MIT
41
+ metadata: {}
42
+ post_install_message:
43
+ rdoc_options: []
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - '>='
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ requirements: []
57
+ rubyforge_project: assets-squasher
58
+ rubygems_version: 2.2.2
59
+ signing_key:
60
+ specification_version: 4
61
+ summary: Assets squasher for single page apps.
62
+ test_files: []