Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(246)

Side by Side Diff: src/caterpillar.py

Issue 1604873003: Added dependency script injection and fixed other script injection. (Closed) Base URL: sso://user/alger/caterpillar@master
Patch Set: Merge Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python2 1 #!/usr/bin/env python2
2 # -*- coding: utf-8 -*- 2 # -*- coding: utf-8 -*-
3 3
4 # Copyright 2015 Google Inc. All Rights Reserved. 4 # Copyright 2015 Google Inc. All Rights Reserved.
5 # 5 #
6 # Licensed under the Apache License, Version 2.0 (the "License"); 6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License. 7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at 8 # You may obtain a copy of the License at
9 # 9 #
10 # http://www.apache.org/licenses/LICENSE-2.0 10 # http://www.apache.org/licenses/LICENSE-2.0
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
61 61
62 # Name of the service worker static script. 62 # Name of the service worker static script.
63 SW_STATIC_SCRIPT_NAME = 'sw_static.js' 63 SW_STATIC_SCRIPT_NAME = 'sw_static.js'
64 64
65 # Largest number that the cache version can be. 65 # Largest number that the cache version can be.
66 MAX_CACHE_VERSION = 1000000 66 MAX_CACHE_VERSION = 1000000
67 67
68 # Where this file is located (so we can find resources). 68 # Where this file is located (so we can find resources).
69 SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) 69 SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
70 70
71 # Maps dependency managers to the folder they install dependencies into.
72 DEPENDENCY_MANAGER_INSTALL_FOLDER = {
73 'bower': 'bower_components',
74 'npm': 'node_modules',
75 }
76
71 SW_FORMAT_STRING = """/** 77 SW_FORMAT_STRING = """/**
72 * @file Service worker generated by Caterpillar. 78 * @file Service worker generated by Caterpillar.
73 */ 79 */
74 80
75 /** 81 /**
76 * @const Current cache version. 82 * @const Current cache version.
77 * 83 *
78 * Increment this to force cache to clear. 84 * Increment this to force cache to clear.
79 */ 85 */
80 var CACHE_VERSION = {cache_version}; 86 var CACHE_VERSION = {cache_version};
(...skipping 245 matching lines...) Expand 10 before | Expand all | Expand 10 after
326 output_dir: Directory of the web app to insert TODOs into. 332 output_dir: Directory of the web app to insert TODOs into.
327 """ 333 """
328 logging.debug('Inserting TODOs.') 334 logging.debug('Inserting TODOs.')
329 dirwalk = os.walk(output_dir) 335 dirwalk = os.walk(output_dir)
330 for (dirpath, _, filenames) in dirwalk: 336 for (dirpath, _, filenames) in dirwalk:
331 for filename in filenames: 337 for filename in filenames:
332 if filename.endswith('.js'): 338 if filename.endswith('.js'):
333 path = os.path.join(dirpath, filename) 339 path = os.path.join(dirpath, filename)
334 insert_todos_into_file(path) 340 insert_todos_into_file(path)
335 341
336 def generate_service_worker(output_dir, ca_manifest, polyfill_paths, 342 def generate_service_worker(output_dir, ca_manifest, required_js_paths,
337 boilerplate_dir): 343 boilerplate_dir):
338 """Generates code for a service worker. 344 """Generates code for a service worker.
339 345
340 Args: 346 Args:
341 output_dir: Directory of the web app that this service worker will run in. 347 output_dir: Directory of the web app that this service worker will run in.
342 ca_manifest: Chrome App manifest dictionary. 348 ca_manifest: Chrome App manifest dictionary.
343 polyfill_paths: List of paths to required polyfill scripts, relative to the 349 required_js_paths: List of paths to required scripts, relative to the
344 boilerplate directory. 350 boilerplate directory.
345 boilerplate_dir: Caterpillar script directory within output web app. 351 boilerplate_dir: Caterpillar script directory within output web app.
346 352
347 Returns: 353 Returns:
348 JavaScript string. 354 JavaScript string.
349 """ 355 """
350 # Get the paths of files we will cache. 356 # Get the paths of files we will cache.
351 all_filepaths = [] 357 all_filepaths = []
352 logging.debug('Looking for files to cache.') 358 logging.debug('Looking for files to cache.')
353 dirwalk = os.walk(output_dir) 359 dirwalk = os.walk(output_dir)
354 for (dirpath, _, filenames) in dirwalk: 360 for (dirpath, _, filenames) in dirwalk:
355 # Add the relative file paths of each file to the filepaths list. 361 # Add the relative file paths of each file to the filepaths list.
356 all_filepaths.extend( 362 all_filepaths.extend(
357 os.path.relpath(os.path.join(dirpath, filename), output_dir) 363 os.path.relpath(os.path.join(dirpath, filename), output_dir)
358 for filename in filenames) 364 for filename in filenames)
359 logging.debug('Cached files:\n\t%s', '\n\t'.join(all_filepaths)) 365 logging.debug('Cached files:\n\t%s', '\n\t'.join(all_filepaths))
360 # Format the file paths as JavaScript strings. 366 # Format the file paths as JavaScript strings.
361 all_filepaths = ["'{}'".format(fp) for fp in all_filepaths] 367 all_filepaths = ["'{}'".format(fp) for fp in all_filepaths]
362 368
363 logging.debug('Generating service worker.') 369 logging.debug('Generating service worker.')
364 370
365 sw_js = SW_FORMAT_STRING.format( 371 sw_js = SW_FORMAT_STRING.format(
366 cache_version=random.randrange(MAX_CACHE_VERSION), 372 cache_version=random.randrange(MAX_CACHE_VERSION),
367 joined_filepaths=',\n '.join(all_filepaths), 373 joined_filepaths=',\n '.join(all_filepaths),
368 boilerplate_dir=boilerplate_dir 374 boilerplate_dir=boilerplate_dir
369 ) 375 )
370 376
371 # The polyfills we get as input are relative to the boilerplate directory, but 377 # The polyfills we get as input are relative to the boilerplate directory, but
372 # the service worker is in the root directory, so we need to change the paths. 378 # the service worker is in the root directory, so we need to change the paths.
373 polyfills_paths = [os.path.join(boilerplate_dir, path) 379 required_js_paths = [os.path.join(boilerplate_dir, path)
374 for path in polyfill_paths] 380 for path in required_js_paths]
375 381
376 background_scripts = ca_manifest['app']['background'].get('scripts', []) 382 background_scripts = ca_manifest['app']['background'].get('scripts', [])
377 for script in polyfill_paths + background_scripts: 383 for script in required_js_paths + background_scripts:
378 logging.debug('Importing `%s` to the service worker.', script) 384 logging.debug('Importing `%s` to the service worker.', script)
379 sw_js += "importScripts('{}');\n".format(script) 385 sw_js += "importScripts('{}');\n".format(script)
380 386
381 return sw_js 387 return sw_js
382 388
383 def copy_script(script, directory): 389 def copy_script(script, directory):
384 """Copies a script from Caterpillar into the given directory. 390 """Copies a script from Caterpillar into the given directory.
385 391
386 Args: 392 Args:
387 script: Caterpillar JavaScript filename. 393 script: Caterpillar JavaScript filename.
388 directory: Path to directory. 394 directory: Path to directory.
389 """ 395 """
390 path = os.path.join(SCRIPT_DIR, 'js', script) 396 path = os.path.join(SCRIPT_DIR, 'js', script)
391 new_path = os.path.join(directory, script) 397 new_path = os.path.join(directory, script)
392 logging.debug('Writing `%s` to `%s`.', path, new_path) 398 logging.debug('Writing `%s` to `%s`.', path, new_path)
393 shutil.copyfile(path, new_path) 399 shutil.copyfile(path, new_path)
394 400
395 def add_service_worker(output_dir, ca_manifest, polyfill_paths, 401 def add_service_worker(output_dir, ca_manifest, required_js_paths,
396 boilerplate_dir): 402 boilerplate_dir):
397 """Adds service worker scripts to a web app. 403 """Adds service worker scripts to a web app.
398 404
399 Args: 405 Args:
400 output_dir: Path to web app to add service worker scripts to. 406 output_dir: Path to web app to add service worker scripts to.
401 ca_manifest: Chrome App manifest dictionary. 407 ca_manifest: Chrome App manifest dictionary.
402 polyfill_paths: List of paths to required polyfill scripts, relative to the 408 required_js_paths: List of paths to required scripts, relative to the
403 boilerplate directory. 409 boilerplate directory.
404 boilerplate_dir: Caterpillar script directory within web app. 410 boilerplate_dir: Caterpillar script directory within web app.
405 """ 411 """
406 # We have to copy the other scripts before we generate the service worker 412 # We have to copy the other scripts before we generate the service worker
407 # caching script, or else they won't be cached. 413 # caching script, or else they won't be cached.
408 boilerplate_path = os.path.join(output_dir, boilerplate_dir) 414 boilerplate_path = os.path.join(output_dir, boilerplate_dir)
409 copy_script(REGISTER_SCRIPT_NAME, boilerplate_path) 415 copy_script(REGISTER_SCRIPT_NAME, boilerplate_path)
410 copy_script(SW_STATIC_SCRIPT_NAME, boilerplate_path) 416 copy_script(SW_STATIC_SCRIPT_NAME, boilerplate_path)
411 417
412 sw_js = generate_service_worker(output_dir, ca_manifest, polyfill_paths, 418 sw_js = generate_service_worker(output_dir, ca_manifest, required_js_paths,
413 boilerplate_dir) 419 boilerplate_dir)
414 420
415 # We can now write the service worker. Note that it must be in the root. 421 # We can now write the service worker. Note that it must be in the root.
416 sw_path = os.path.join(output_dir, SW_SCRIPT_NAME) 422 sw_path = os.path.join(output_dir, SW_SCRIPT_NAME)
417 logging.debug('Writing service worker to `%s`.', sw_path) 423 logging.debug('Writing service worker to `%s`.', sw_path)
418 with open(sw_path, 'w') as sw_file: 424 with open(sw_path, 'w') as sw_file:
419 sw_file.write(sw_js) 425 sw_file.write(sw_js)
420 426
421 class InstallationError(Exception): 427 class InstallationError(Exception):
422 """Exception raised when a dependency fails to install.""" 428 """Exception raised when a dependency fails to install."""
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after
556 logging.info('Found Chrome APIs: %s', ', '.join(apis)) 562 logging.info('Found Chrome APIs: %s', ', '.join(apis))
557 563
558 # Determine which Chrome Apps APIs can be polyfilled, and which cannot. 564 # Determine which Chrome Apps APIs can be polyfilled, and which cannot.
559 polyfillable = [] 565 polyfillable = []
560 not_polyfillable = [] 566 not_polyfillable = []
561 for api in apis: 567 for api in apis:
562 if api in POLYFILLS: 568 if api in POLYFILLS:
563 polyfillable.append(api) 569 polyfillable.append(api)
564 else: 570 else:
565 not_polyfillable.append(api) 571 not_polyfillable.append(api)
572 if 'runtime' not in polyfillable:
Matt Giuca 2016/01/21 00:23:40 why?
Matthew Alger 2016/01/21 00:29:30 I don't want to add it twice, and polyfillable is
Matt Giuca 2016/01/27 00:35:25 I mean why do you special-case runtime here? (Why
Matthew Alger 2016/01/27 00:36:38 All Chrome Apps errors are stored in chrome.runtim
573 polyfillable.insert(0, 'runtime')
566 574
567 logging.info('Polyfilled Chrome APIs: %s', ', '.join(polyfillable)) 575 logging.info('Polyfilled Chrome APIs: %s', ', '.join(polyfillable))
568 logging.warning('Could not polyfill Chrome APIs: %s', 576 logging.warning('Could not polyfill Chrome APIs: %s',
569 ', '.join(not_polyfillable)) 577 ', '.join(not_polyfillable))
570 578
579 # Read in the polyfill manifests and store their dependencies. We can't
580 # install them yet, though, since that has to be done after editing code or
581 # the dependencies will also be edited.
582 polyfill_manifests = polyfill_manifest.load_many(polyfillable)
583 dependencies = [dependency
584 for manifest in polyfill_manifests.values()
585 for dependency in manifest['dependencies']]
586
571 # List of paths of static code to be copied from Caterpillar into the output 587 # List of paths of static code to be copied from Caterpillar into the output
572 # web app, relative to Caterpillar's JS source directory. 588 # web app, relative to Caterpillar's JS source directory.
573 required_js_paths = [ 589 required_always_paths = [
Matt Giuca 2016/01/21 00:23:40 Why the name change?
Matthew Alger 2016/01/21 00:29:30 Added three different lists with a similar name -
Matt Giuca 2016/01/27 00:35:25 Acknowledged.
574 'caterpillar.js', 590 'caterpillar.js',
575 REGISTER_SCRIPT_NAME, 591 REGISTER_SCRIPT_NAME,
576 ] + polyfill_paths(polyfillable) 592 ]
593
594 # The dependencies and polyfills are also requirements, but we need to handle
595 # them differently, so they're split up into two lists.
596 required_dependency_paths = []
597 for dependency in dependencies:
598 # Note that dependencies are installed into the root, but we need paths
599 # relative to Caterpillar's boilerplate directory.
600 dependency_path = os.path.join('..',
601 DEPENDENCY_MANAGER_INSTALL_FOLDER[dependency['manager']],
602 dependency['name'], dependency['path'])
603 required_dependency_paths.append(dependency_path)
604
605 required_polyfill_paths = polyfill_paths(polyfillable)
577 606
578 # Read in and check the manifest file. 607 # Read in and check the manifest file.
579 try: 608 try:
580 ca_manifest = chrome_app.manifest.get(input_dir) 609 ca_manifest = chrome_app.manifest.get(input_dir)
581 chrome_app.manifest.verify(ca_manifest) 610 chrome_app.manifest.verify(ca_manifest)
582 except ValueError as e: 611 except ValueError as e:
583 logging.error(e.message) 612 logging.error(e.message)
584 return 613 return
585 614
586 # TODO(alger): Identify background scripts and determine start_url. 615 # TODO(alger): Identify background scripts and determine start_url.
587 start_url = config['start_url'] 616 start_url = config['start_url']
588 logging.info('Got start URL from config file: `%s`', start_url) 617 logging.info('Got start URL from config file: `%s`', start_url)
589 618
590 # Generate a progressive web app manifest. 619 # Generate a progressive web app manifest.
591 pwa_manifest = generate_web_manifest(ca_manifest, start_url) 620 pwa_manifest = generate_web_manifest(ca_manifest, start_url)
592 pwa_manifest_path = os.path.join(output_dir, PWA_MANIFEST_FILENAME) 621 pwa_manifest_path = os.path.join(output_dir, PWA_MANIFEST_FILENAME)
593 with open(pwa_manifest_path, 'w') as pwa_manifest_file: 622 with open(pwa_manifest_path, 'w') as pwa_manifest_file:
594 json.dump(pwa_manifest, pwa_manifest_file, indent=4, sort_keys=True) 623 json.dump(pwa_manifest, pwa_manifest_file, indent=4, sort_keys=True)
595 logging.debug('Wrote `%s` to `%s`.', PWA_MANIFEST_FILENAME, pwa_manifest_path) 624 logging.debug('Wrote `%s` to `%s`.', PWA_MANIFEST_FILENAME, pwa_manifest_path)
596 625
597 # Remove unnecessary files from the output web app. This must be done before 626 # Remove unnecessary files from the output web app. This must be done before
598 # the service worker is generated, or these files will be cached. 627 # the service worker is generated, or these files will be cached.
599 cleanup_output_dir(output_dir) 628 cleanup_output_dir(output_dir)
600 629
601 # Edit the HTML and JS code of the output web app. 630 # Edit the HTML and JS code of the output web app.
602 # This is adding TODOs, injecting tags, etc. - anything that involves editing 631 # This is adding TODOs, injecting tags, etc. - anything that involves editing
603 # user code directly. This must be done before the static code is copied 632 # user code directly. This must be done before the static code is copied
604 # across, or the polyfills will have TODOs added to them. 633 # across, or the polyfills will have TODOs added to them.
605 edit_code(output_dir, required_js_paths, ca_manifest, config) 634 required_script_paths = (required_always_paths + required_dependency_paths
635 + required_polyfill_paths) # Order significant here.
Matt Giuca 2016/01/21 00:23:40 nit: Don't squeeze this comment here; put it above
Matthew Alger 2016/01/21 00:29:30 Done.
636 edit_code(output_dir, required_script_paths, ca_manifest, config)
606 637
607 # We want the static SW file to be copied in too, so we add it here. 638 # We want the static SW file to be copied in too, so we add it here.
608 # We have to add it after edit_code or it would be included in the HTML, but 639 # We have to add it after edit_code or it would be included in the HTML, but
609 # this is service worker-only code, and shouldn't be included there. 640 # this is service worker-only code, and shouldn't be included there.
610 required_js_paths.append(SW_STATIC_SCRIPT_NAME) 641 required_always_paths.append(SW_STATIC_SCRIPT_NAME)
611 642
612 # Copy static code from Caterpillar into the output web app. 643 # Copy static code from Caterpillar into the output web app.
613 # This must be done before the service worker is generated, or these files 644 # This must be done before the service worker is generated, or these files
614 # will not be cached. 645 # will not be cached.
615 copy_static_code(required_js_paths, output_dir, boilerplate_dir) 646 required_static_paths = required_always_paths + required_polyfill_paths
647 copy_static_code(required_static_paths, output_dir, boilerplate_dir)
616 648
617 # Read in the polyfill manifests and install polyfill dependencies. 649 # Install the polyfill dependencies. This must be done before the service
618 # This must be done after editing code (or the dependencies will also be 650 # worker is generated, or the dependencies won't be cached.
619 # edited) and before the service worker is generated (or the dependencies
620 # won't be cached).
621 polyfill_manifests = polyfill_manifest.load_many(polyfillable)
622 dependencies = [dependency
623 for manifest in polyfill_manifests.values()
624 for dependency in manifest['dependencies']]
625 try: 651 try:
626 install_dependencies(dependencies, output_dir) 652 install_dependencies(dependencies, output_dir)
627 except ValueError as e: 653 except ValueError as e:
628 logging.error(e.message) 654 logging.error(e.message)
629 return 655 return
630 656
631 # Generate and write a service worker. 657 # Generate and write a service worker.
632 add_service_worker(output_dir, ca_manifest, polyfill_paths(polyfillable), 658 required_sw_paths = required_dependency_paths + required_polyfill_paths
659 add_service_worker(output_dir, ca_manifest, required_sw_paths,
633 boilerplate_dir) 660 boilerplate_dir)
634 661
635 logging.info('Conversion complete.') 662 logging.info('Conversion complete.')
636 663
637 class Formatter(logging.Formatter): 664 class Formatter(logging.Formatter):
638 """Caterpillar logging formatter. 665 """Caterpillar logging formatter.
639 666
640 Adds color to the logged information. 667 Adds color to the logged information.
641 """ 668 """
642 def format(self, record): 669 def format(self, record):
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
693 # Main program. 720 # Main program.
694 if args.mode == 'config': 721 if args.mode == 'config':
695 configuration.generate_and_save(args.output, args.interactive) 722 configuration.generate_and_save(args.output, args.interactive)
696 723
697 elif args.mode == 'convert': 724 elif args.mode == 'convert':
698 config = configuration.load(args.config) 725 config = configuration.load(args.config)
699 convert_app(args.input, args.output, config, args.force) 726 convert_app(args.input, args.output, config, args.force)
700 727
701 if __name__ == '__main__': 728 if __name__ == '__main__':
702 sys.exit(main()) 729 sys.exit(main())
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698