assets-squasher 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []