OLD | NEW |
| (Empty) |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 /// Script to create boilerplate for a Polymer element. | |
6 /// Produces .dart and .html files for the element. | |
7 /// | |
8 /// Run this script with pub run: | |
9 /// | |
10 /// pub run polymer:new_element element-name [-o output_dir] | |
11 /// | |
12 library polymer.bin.new_element; | |
13 | |
14 import 'dart:io'; | |
15 import 'package:args/args.dart'; | |
16 import 'package:path/path.dart' as path show absolute, dirname, join, split; | |
17 import 'package:polymer/html_element_names.dart'; | |
18 | |
19 void printUsage(ArgParser parser) { | |
20 print('pub run polymer:new_element [-o output_dir] [-e super-element] ' | |
21 'element-name'); | |
22 print(parser.getUsage()); | |
23 } | |
24 | |
25 void main(List<String> args) { | |
26 var parser = new ArgParser(allowTrailingOptions: true); | |
27 | |
28 parser.addOption('output-dir', abbr: 'o', help: 'Output directory'); | |
29 parser.addOption('extends', | |
30 abbr: 'e', | |
31 help: 'Extends polymer-element or DOM element (e.g., div, span)'); | |
32 parser.addFlag('help', abbr: 'h'); | |
33 | |
34 var options, element; | |
35 try { | |
36 options = parser.parse(args); | |
37 if (options['help']) { | |
38 printUsage(parser); | |
39 return; | |
40 } | |
41 if (options.rest == null || options.rest.isEmpty) { | |
42 throw new FormatException('No element specified'); | |
43 } | |
44 element = options.rest[0]; | |
45 if (!_isPolymerElement(element)) { | |
46 throw new FormatException('Must specify polymer-element to create.\n' | |
47 'polymer-element must be all lowercase with at least 1 hyphen.'); | |
48 } | |
49 } catch (e) { | |
50 print('$e\n'); | |
51 printUsage(parser); | |
52 exitCode = 1; | |
53 return; | |
54 } | |
55 | |
56 var outputDir, startDir; | |
57 | |
58 var outputPath = options['output-dir']; | |
59 | |
60 if (outputPath == null) { | |
61 if ((new File('pubspec.yaml')).existsSync()) { | |
62 print('When creating elements in root directory of package, ' | |
63 '-o <dir> must be specified'); | |
64 exitCode = 1; | |
65 return; | |
66 } | |
67 outputDir = (new Directory('.')).resolveSymbolicLinksSync(); | |
68 } else { | |
69 var outputDirLocation = new Directory(outputPath); | |
70 if (!outputDirLocation.existsSync()) { | |
71 outputDirLocation.createSync(recursive: true); | |
72 } | |
73 outputDir = (new Directory(outputPath)).resolveSymbolicLinksSync(); | |
74 } | |
75 | |
76 var pubspecDir = _findDirWithFile(outputDir, 'pubspec.yaml'); | |
77 | |
78 if (pubspecDir == null) { | |
79 print('Could not find pubspec.yaml when walking up from $outputDir'); | |
80 exitCode = 1; | |
81 return; | |
82 } | |
83 | |
84 var length = path.split(pubspecDir).length; | |
85 var distanceToPackageRoot = path.split(outputDir).length - length; | |
86 | |
87 // See dartbug.com/20076 for the algorithm used here. | |
88 if (distanceToPackageRoot > 0) { | |
89 if (path.split(outputDir)[length] == 'lib') { | |
90 distanceToPackageRoot++; | |
91 } else { | |
92 distanceToPackageRoot--; | |
93 } | |
94 } | |
95 | |
96 var superElement = options['extends']; | |
97 | |
98 if ((superElement == null) || | |
99 _isDOMElement(superElement) || | |
100 _isPolymerElement(superElement)) { | |
101 try { | |
102 _createBoilerPlate( | |
103 element, options['extends'], outputDir, distanceToPackageRoot); | |
104 } on Exception catch (e, t) { | |
105 print('Error creating files in $outputDir'); | |
106 print('$e $t'); | |
107 exitCode = 1; | |
108 return; | |
109 } | |
110 } else { | |
111 if (superElement.contains('-')) { | |
112 print('Extending invalid element "$superElement". Polymer elements ' | |
113 'may contain only lowercase letters at least one hyphen.'); | |
114 } else { | |
115 print('Extending invalid element "$superElement". $superElement is not ' | |
116 ' a builtin DOM type.'); | |
117 } | |
118 exitCode = 1; | |
119 return; | |
120 } | |
121 | |
122 return; | |
123 } | |
124 | |
125 String _findDirWithFile(String dir, String filename) { | |
126 while (!new File(path.join(dir, filename)).existsSync()) { | |
127 var parentDir = path.dirname(dir); | |
128 // If we reached root and failed to find it, bail. | |
129 if (parentDir == dir) return null; | |
130 dir = parentDir; | |
131 } | |
132 return dir; | |
133 } | |
134 | |
135 bool _isDOMElement(String element) => (HTML_ELEMENT_NAMES[element] != null); | |
136 | |
137 bool _isPolymerElement(String element) { | |
138 return element.contains('-') && (element.toLowerCase() == element); | |
139 } | |
140 | |
141 String _toCamelCase(String s) { | |
142 return s[0].toUpperCase() + s.substring(1); | |
143 } | |
144 | |
145 void _createBoilerPlate(String element, String superClass, String directory, | |
146 int distanceToPackageRoot) { | |
147 var segments = element.split('-'); | |
148 var capitalizedName = segments.map((e) => _toCamelCase(e)).join(''); | |
149 var underscoreName = element.replaceAll('-', '_'); | |
150 var pathToPackages = '../' * distanceToPackageRoot; | |
151 | |
152 bool superClassIsPolymer = | |
153 (superClass == null ? false : _isPolymerElement(superClass)); | |
154 | |
155 var classDeclaration = ''; | |
156 var importDartHtml = ''; | |
157 var polymerCreatedString = ''; | |
158 var extendsElementString = ''; | |
159 var shadowString = ''; | |
160 | |
161 if (superClass == null) { | |
162 classDeclaration = '\nclass $capitalizedName extends PolymerElement {'; | |
163 } else if (superClassIsPolymer) { | |
164 // The element being extended is a PolymerElement. | |
165 var camelSuperClass = | |
166 superClass.split('-').map((e) => _toCamelCase(e)).join(''); | |
167 classDeclaration = 'class $capitalizedName extends $camelSuperClass {'; | |
168 extendsElementString = ' extends="$superClass"'; | |
169 shadowString = '\n <!-- Render extended element\'s Shadow DOM here -->\n' | |
170 ' <shadow>\n </shadow>'; | |
171 } else { | |
172 // The element being extended is a DOM Class. | |
173 importDartHtml = "import 'dart:html';\n"; | |
174 classDeclaration = | |
175 'class $capitalizedName extends ${HTML_ELEMENT_NAMES[superClass]} ' | |
176 'with Polymer, Observable {'; | |
177 polymerCreatedString = '\n polymerCreated();'; | |
178 extendsElementString = ' extends="$superClass"'; | |
179 } | |
180 | |
181 String html = ''' | |
182 <!-- import polymer-element's definition --> | |
183 <link rel="import" href="${pathToPackages}packages/polymer/polymer.html"> | |
184 | |
185 <polymer-element name="$element"$extendsElementString> | |
186 <template> | |
187 <style> | |
188 :host { | |
189 display: block; | |
190 } | |
191 </style>$shadowString | |
192 <!-- Template content here --> | |
193 </template> | |
194 <script type="application/dart" src="${underscoreName}.dart"></script> | |
195 </polymer-element> | |
196 '''; | |
197 | |
198 String htmlFile = path.join(directory, underscoreName + '.html'); | |
199 new File(htmlFile).writeAsStringSync(html); | |
200 | |
201 String dart = ''' | |
202 ${importDartHtml}import 'package:polymer/polymer.dart'; | |
203 | |
204 /** | |
205 * A Polymer $element element. | |
206 */ | |
207 @CustomTag('$element') | |
208 $classDeclaration | |
209 | |
210 /// Constructor used to create instance of ${capitalizedName}. | |
211 ${capitalizedName}.created() : super.created() {$polymerCreatedString | |
212 } | |
213 | |
214 /* | |
215 * Optional lifecycle methods - uncomment if needed. | |
216 * | |
217 | |
218 /// Called when an instance of $element is inserted into the DOM. | |
219 attached() { | |
220 super.attached(); | |
221 } | |
222 | |
223 /// Called when an instance of $element is removed from the DOM. | |
224 detached() { | |
225 super.detached(); | |
226 } | |
227 | |
228 /// Called when an attribute (such as a class) of an instance of | |
229 /// $element is added, changed, or removed. | |
230 attributeChanged(String name, String oldValue, String newValue) { | |
231 } | |
232 | |
233 /// Called when $element has been fully prepared (Shadow DOM created, | |
234 /// property observers set up, event listeners attached). | |
235 ready() { | |
236 } | |
237 | |
238 */ | |
239 | |
240 } | |
241 '''; | |
242 | |
243 String dartFile = path.join(directory, underscoreName + '.dart'); | |
244 new File(dartFile).writeAsStringSync(dart); | |
245 | |
246 print('Successfully created:'); | |
247 print(' ' + path.absolute(path.join(directory, underscoreName + '.dart'))); | |
248 print(' ' + path.absolute(path.join(directory, underscoreName + '.html'))); | |
249 } | |
OLD | NEW |