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 |