1
+ // #docregion
2
+ 'use strict' ;
3
+
4
+ const fs = require ( 'fs' ) ;
5
+ const path = require ( 'path' ) ;
6
+ const glob = require ( 'glob' ) ;
7
+
8
+ /**
9
+ * Simple Promiseify function that takes a Node API and return a version that supports promises.
10
+ * We use promises instead of synchronized functions to make the process less I/O bound and
11
+ * faster. It also simplify the code.
12
+ */
13
+ function promiseify ( fn ) {
14
+ return function ( ) {
15
+ const args = [ ] . slice . call ( arguments , 0 ) ;
16
+ return new Promise ( ( resolve , reject ) => {
17
+ fn . apply ( this , args . concat ( [ function ( err , value ) {
18
+ if ( err ) {
19
+ reject ( err ) ;
20
+ } else {
21
+ resolve ( value ) ;
22
+ }
23
+ } ] ) ) ;
24
+ } ) ;
25
+ } ;
26
+ }
27
+
28
+ const readFile = promiseify ( fs . readFile ) ;
29
+ const writeFile = promiseify ( fs . writeFile ) ;
30
+
31
+
32
+ function inlineResources ( globs ) {
33
+ if ( typeof globs == 'string' ) {
34
+ globs = [ globs ] ;
35
+ }
36
+
37
+ /**
38
+ * For every argument, inline the templates and styles under it and write the new file.
39
+ */
40
+ return Promise . all ( globs . map ( pattern => {
41
+ if ( pattern . indexOf ( '*' ) < 0 ) {
42
+ // Argument is a directory target, add glob patterns to include every files.
43
+ pattern = path . join ( pattern , '**' , '*' ) ;
44
+ }
45
+
46
+ const files = glob . sync ( pattern , { } )
47
+ . filter ( name => / \. j s $ / . test ( name ) ) ; // Matches only JavaScript files.
48
+
49
+ // Generate all files content with inlined templates.
50
+ return Promise . all ( files . map ( filePath => {
51
+ return readFile ( filePath , 'utf-8' )
52
+ . then ( content => inlineResourcesFromString ( content , url => {
53
+ return path . join ( path . dirname ( filePath ) , url ) ;
54
+ } ) )
55
+ . then ( content => writeFile ( filePath , content ) )
56
+ . catch ( err => {
57
+ console . error ( 'An error occured: ' , err ) ;
58
+ } ) ;
59
+ } ) ) ;
60
+ } ) ) ;
61
+ }
62
+
63
+ /**
64
+ * Inline resources from a string content.
65
+ * @param content {string} The source file's content.
66
+ * @param urlResolver {Function} A resolver that takes a URL and return a path.
67
+ * @returns {string } The content with resources inlined.
68
+ */
69
+ function inlineResourcesFromString ( content , urlResolver ) {
70
+ // Curry through the inlining functions.
71
+ return [
72
+ inlineTemplate ,
73
+ inlineStyle ,
74
+ removeModuleId
75
+ ] . reduce ( ( content , fn ) => fn ( content , urlResolver ) , content ) ;
76
+ }
77
+
78
+ if ( require . main === module ) {
79
+ inlineResources ( process . argv . slice ( 2 ) ) ;
80
+ }
81
+
82
+
83
+ /**
84
+ * Inline the templates for a source file. Simply search for instances of `templateUrl: ...` and
85
+ * replace with `template: ...` (with the content of the file included).
86
+ * @param content {string} The source file's content.
87
+ * @param urlResolver {Function} A resolver that takes a URL and return a path.
88
+ * @return {string } The content with all templates inlined.
89
+ */
90
+ function inlineTemplate ( content , urlResolver ) {
91
+ return content . replace ( / t e m p l a t e U r l : \s * ' ( [ ^ ' ] + ?\. h t m l ) ' / g, function ( m , templateUrl ) {
92
+ const templateFile = urlResolver ( templateUrl ) ;
93
+ const templateContent = fs . readFileSync ( templateFile , 'utf-8' ) ;
94
+ const shortenedTemplate = templateContent
95
+ . replace ( / ( [ \n \r ] \s * ) + / gm, ' ' )
96
+ . replace ( / " / g, '\\"' ) ;
97
+ return `template: "${ shortenedTemplate } "` ;
98
+ } ) ;
99
+ }
100
+
101
+
102
+ /**
103
+ * Inline the styles for a source file. Simply search for instances of `styleUrls: [...]` and
104
+ * replace with `styles: [...]` (with the content of the file included).
105
+ * @param urlResolver {Function} A resolver that takes a URL and return a path.
106
+ * @param content {string} The source file's content.
107
+ * @return {string } The content with all styles inlined.
108
+ */
109
+ function inlineStyle ( content , urlResolver ) {
110
+ return content . replace ( / s t y l e U r l s : \s * ( \[ [ \s \S ] * ?\] ) / gm, function ( m , styleUrls ) {
111
+ const urls = eval ( styleUrls ) ;
112
+ return 'styles: ['
113
+ + urls . map ( styleUrl => {
114
+ const styleFile = urlResolver ( styleUrl ) ;
115
+ const styleContent = fs . readFileSync ( styleFile , 'utf-8' ) ;
116
+ const shortenedStyle = styleContent
117
+ . replace ( / ( [ \n \r ] \s * ) + / gm, ' ' )
118
+ . replace ( / " / g, '\\"' ) ;
119
+ return `"${ shortenedStyle } "` ;
120
+ } )
121
+ . join ( ',\n' )
122
+ + ']' ;
123
+ } ) ;
124
+ }
125
+
126
+
127
+ /**
128
+ * Remove every mention of `moduleId: module.id`.
129
+ * @param content {string} The source file's content.
130
+ * @returns {string } The content with all moduleId: mentions removed.
131
+ */
132
+ function removeModuleId ( content ) {
133
+ return content . replace ( / \s * m o d u l e I d : \s * m o d u l e \. i d \s * , ? \s * / gm, '' ) ;
134
+ }
135
+
136
+ module . exports = inlineResources ;
137
+ module . exports . inlineResourcesFromString = inlineResourcesFromString ;
0 commit comments