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

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: Reverted runtime dependency 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 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
62 62
63 # Name of the service worker static script. 63 # Name of the service worker static script.
64 SW_STATIC_SCRIPT_NAME = 'sw_static.js' 64 SW_STATIC_SCRIPT_NAME = 'sw_static.js'
65 65
66 # Largest number that the cache version can be. 66 # Largest number that the cache version can be.
67 MAX_CACHE_VERSION = 1000000 67 MAX_CACHE_VERSION = 1000000
68 68
69 # Where this file is located (so we can find resources). 69 # Where this file is located (so we can find resources).
70 SCRIPT_DIR = os.path.dirname(__file__) 70 SCRIPT_DIR = os.path.dirname(__file__)
71 71
72 # Maps dependency managers to the folder they install dependencies into.
73 DEPENDENCY_MANAGER_INSTALL_FOLDER = {
74 'bower': 'bower_components',
75 'npm': 'node_modules',
76 }
77
72 SW_FORMAT_STRING = """/** 78 SW_FORMAT_STRING = """/**
73 * @file Service worker generated by Caterpillar. 79 * @file Service worker generated by Caterpillar.
74 */ 80 */
75 81
76 /** 82 /**
77 * @const Current cache version. 83 * @const Current cache version.
78 * 84 *
79 * Increment this to force cache to clear. 85 * Increment this to force cache to clear.
80 */ 86 */
81 var CACHE_VERSION = {cache_version}; 87 var CACHE_VERSION = {cache_version};
(...skipping 258 matching lines...) Expand 10 before | Expand all | Expand 10 after
340 output_dir: Directory of the web app to insert TODOs into. 346 output_dir: Directory of the web app to insert TODOs into.
341 """ 347 """
342 logging.debug('Inserting TODOs.') 348 logging.debug('Inserting TODOs.')
343 dirwalk = os.walk(output_dir) 349 dirwalk = os.walk(output_dir)
344 for (dirpath, _, filenames) in dirwalk: 350 for (dirpath, _, filenames) in dirwalk:
345 for filename in filenames: 351 for filename in filenames:
346 if filename.endswith('.js'): 352 if filename.endswith('.js'):
347 path = os.path.join(dirpath, filename) 353 path = os.path.join(dirpath, filename)
348 insert_todos_into_file(path) 354 insert_todos_into_file(path)
349 355
350 356 def generate_service_worker(output_dir, ca_manifest, required_js_paths,
351 def generate_service_worker(output_dir, ca_manifest, polyfill_paths,
352 boilerplate_dir): 357 boilerplate_dir):
353 """Generates code for a service worker. 358 """Generates code for a service worker.
354 359
355 Args: 360 Args:
356 output_dir: Directory of the web app that this service worker will run in. 361 output_dir: Directory of the web app that this service worker will run in.
357 ca_manifest: Chrome App manifest dictionary. 362 ca_manifest: Chrome App manifest dictionary.
358 polyfill_paths: List of paths to required polyfill scripts, relative to the 363 required_js_paths: List of paths to required scripts, relative to the
359 boilerplate directory. 364 boilerplate directory.
360 boilerplate_dir: Caterpillar script directory within output web app. 365 boilerplate_dir: Caterpillar script directory within output web app.
361 366
362 Returns: 367 Returns:
363 JavaScript string. 368 JavaScript string.
364 """ 369 """
365 # Get the paths of files we will cache. 370 # Get the paths of files we will cache.
366 all_filepaths = [] 371 all_filepaths = []
367 logging.debug('Looking for files to cache.') 372 logging.debug('Looking for files to cache.')
368 dirwalk = os.walk(output_dir) 373 dirwalk = os.walk(output_dir)
369 for (dirpath, _, filenames) in dirwalk: 374 for (dirpath, _, filenames) in dirwalk:
370 # Add the relative file paths of each file to the filepaths list. 375 # Add the relative file paths of each file to the filepaths list.
371 all_filepaths.extend( 376 all_filepaths.extend(
372 os.path.relpath(os.path.join(dirpath, filename), output_dir) 377 os.path.relpath(os.path.join(dirpath, filename), output_dir)
373 for filename in filenames) 378 for filename in filenames)
374 logging.debug('Cached files:\n\t%s', '\n\t'.join(all_filepaths)) 379 logging.debug('Cached files:\n\t%s', '\n\t'.join(all_filepaths))
375 # Format the file paths as JavaScript strings. 380 # Format the file paths as JavaScript strings.
376 all_filepaths = ["'{}'".format(fp) for fp in all_filepaths] 381 all_filepaths = ["'{}'".format(fp) for fp in all_filepaths]
377 382
378 logging.debug('Generating service worker.') 383 logging.debug('Generating service worker.')
379 384
380 sw_js = SW_FORMAT_STRING.format( 385 sw_js = SW_FORMAT_STRING.format(
381 cache_version=random.randrange(MAX_CACHE_VERSION), 386 cache_version=random.randrange(MAX_CACHE_VERSION),
382 joined_filepaths=',\n '.join(all_filepaths), 387 joined_filepaths=',\n '.join(all_filepaths),
383 boilerplate_dir=boilerplate_dir 388 boilerplate_dir=boilerplate_dir
384 ) 389 )
385 390
386 # The polyfills we get as input are relative to the boilerplate directory, but 391 # The polyfills we get as input are relative to the boilerplate directory, but
387 # the service worker is in the root directory, so we need to change the paths. 392 # the service worker is in the root directory, so we need to change the paths.
388 polyfills_paths = [os.path.join(boilerplate_dir, path) 393 required_js_paths = [os.path.join(boilerplate_dir, path)
389 for path in polyfill_paths] 394 for path in required_js_paths]
390 395
391 background_scripts = ca_manifest['app']['background'].get('scripts', []) 396 background_scripts = ca_manifest['app']['background'].get('scripts', [])
392 for script in polyfill_paths + background_scripts: 397 for script in required_js_paths + background_scripts:
393 logging.debug('Importing `%s` to the service worker.', script) 398 logging.debug('Importing `%s` to the service worker.', script)
394 sw_js += "importScripts('{}');\n".format(script) 399 sw_js += "importScripts('{}');\n".format(script)
395 400
396 return sw_js 401 return sw_js
397 402
398 403
399 def copy_script(script, directory): 404 def copy_script(script, directory):
400 """Copies a script from Caterpillar into the given directory. 405 """Copies a script from Caterpillar into the given directory.
401 406
402 Args: 407 Args:
403 script: Caterpillar JavaScript filename. 408 script: Caterpillar JavaScript filename.
404 directory: Path to directory. 409 directory: Path to directory.
405 """ 410 """
406 path = os.path.join(SCRIPT_DIR, 'js', script) 411 path = os.path.join(SCRIPT_DIR, 'js', script)
407 new_path = os.path.join(directory, script) 412 new_path = os.path.join(directory, script)
408 logging.debug('Writing `%s` to `%s`.', path, new_path) 413 logging.debug('Writing `%s` to `%s`.', path, new_path)
409 shutil.copyfile(path, new_path) 414 shutil.copyfile(path, new_path)
410 415
411 416 def add_service_worker(output_dir, ca_manifest, required_js_paths,
412 def add_service_worker(output_dir, ca_manifest, polyfill_paths,
413 boilerplate_dir): 417 boilerplate_dir):
414 """Adds service worker scripts to a web app. 418 """Adds service worker scripts to a web app.
415 419
416 Args: 420 Args:
417 output_dir: Path to web app to add service worker scripts to. 421 output_dir: Path to web app to add service worker scripts to.
418 ca_manifest: Chrome App manifest dictionary. 422 ca_manifest: Chrome App manifest dictionary.
419 polyfill_paths: List of paths to required polyfill scripts, relative to the 423 required_js_paths: List of paths to required scripts, relative to the
420 boilerplate directory. 424 boilerplate directory.
421 boilerplate_dir: Caterpillar script directory within web app. 425 boilerplate_dir: Caterpillar script directory within web app.
422 """ 426 """
423 # We have to copy the other scripts before we generate the service worker 427 # We have to copy the other scripts before we generate the service worker
424 # caching script, or else they won't be cached. 428 # caching script, or else they won't be cached.
425 boilerplate_path = os.path.join(output_dir, boilerplate_dir) 429 boilerplate_path = os.path.join(output_dir, boilerplate_dir)
426 copy_script(REGISTER_SCRIPT_NAME, boilerplate_path) 430 copy_script(REGISTER_SCRIPT_NAME, boilerplate_path)
427 copy_script(SW_STATIC_SCRIPT_NAME, boilerplate_path) 431 copy_script(SW_STATIC_SCRIPT_NAME, boilerplate_path)
428 432
429 sw_js = generate_service_worker(output_dir, ca_manifest, polyfill_paths, 433 sw_js = generate_service_worker(output_dir, ca_manifest, required_js_paths,
430 boilerplate_dir) 434 boilerplate_dir)
431 435
432 # We can now write the service worker. Note that it must be in the root. 436 # We can now write the service worker. Note that it must be in the root.
433 sw_path = os.path.join(output_dir, SW_SCRIPT_NAME) 437 sw_path = os.path.join(output_dir, SW_SCRIPT_NAME)
434 logging.debug('Writing service worker to `%s`.', sw_path) 438 logging.debug('Writing service worker to `%s`.', sw_path)
435 with open(sw_path, 'w') as sw_file: 439 with open(sw_path, 'w') as sw_file:
436 sw_file.write(surrogateescape.encode(sw_js)) 440 sw_file.write(surrogateescape.encode(sw_js))
437 441
438 442
439 class InstallationError(Exception): 443 class InstallationError(Exception):
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after
586 for api in apis: 590 for api in apis:
587 if api in POLYFILLS: 591 if api in POLYFILLS:
588 polyfillable.append(api) 592 polyfillable.append(api)
589 else: 593 else:
590 not_polyfillable.append(api) 594 not_polyfillable.append(api)
591 595
592 logging.info('Polyfilled Chrome APIs: %s', ', '.join(polyfillable)) 596 logging.info('Polyfilled Chrome APIs: %s', ', '.join(polyfillable))
593 logging.warning('Could not polyfill Chrome APIs: %s', 597 logging.warning('Could not polyfill Chrome APIs: %s',
594 ', '.join(not_polyfillable)) 598 ', '.join(not_polyfillable))
595 599
600 # Read in the polyfill manifests and store their dependencies. We can't
601 # install them yet, though, since that has to be done after editing code or
602 # the dependencies will also be edited.
603 polyfill_manifests = polyfill_manifest.load_many(polyfillable)
604 dependencies = [dependency
605 for manifest in polyfill_manifests.values()
606 for dependency in manifest['dependencies']]
607
596 # List of paths of static code to be copied from Caterpillar into the output 608 # List of paths of static code to be copied from Caterpillar into the output
597 # web app, relative to Caterpillar's JS source directory. 609 # web app, relative to Caterpillar's JS source directory.
598 required_js_paths = [ 610 required_always_paths = [
599 'caterpillar.js', 611 'caterpillar.js',
600 REGISTER_SCRIPT_NAME, 612 REGISTER_SCRIPT_NAME,
601 ] + polyfill_paths(polyfillable) 613 ]
614
615 # The dependencies and polyfills are also requirements, but we need to handle
616 # them differently, so they're split up into two lists.
617 required_dependency_paths = []
618 for dependency in dependencies:
619 # Note that dependencies are installed into the root, but we need paths
620 # relative to Caterpillar's boilerplate directory.
621 dependency_path = os.path.join('..',
622 DEPENDENCY_MANAGER_INSTALL_FOLDER[dependency['manager']],
623 dependency['name'], dependency['path'])
624 required_dependency_paths.append(dependency_path)
625
626 required_polyfill_paths = polyfill_paths(polyfillable)
602 627
603 # Read in and check the manifest file. 628 # Read in and check the manifest file.
604 try: 629 try:
605 ca_manifest = chrome_app.manifest.get(input_dir) 630 ca_manifest = chrome_app.manifest.get(input_dir)
606 chrome_app.manifest.localize(ca_manifest, input_dir) 631 chrome_app.manifest.localize(ca_manifest, input_dir)
607 chrome_app.manifest.verify(ca_manifest) 632 chrome_app.manifest.verify(ca_manifest)
608 except ValueError as e: 633 except ValueError as e:
609 logging.error(e.message) 634 logging.error(e.message)
610 return 635 return
611 636
612 # TODO(alger): Identify background scripts and determine start_url. 637 # TODO(alger): Identify background scripts and determine start_url.
613 start_url = config['start_url'] 638 start_url = config['start_url']
614 logging.info('Got start URL from config file: `%s`', start_url) 639 logging.info('Got start URL from config file: `%s`', start_url)
615 640
616 # Generate a progressive web app manifest. 641 # Generate a progressive web app manifest.
617 pwa_manifest = generate_web_manifest(ca_manifest, start_url) 642 pwa_manifest = generate_web_manifest(ca_manifest, start_url)
618 pwa_manifest_path = os.path.join(output_dir, PWA_MANIFEST_FILENAME) 643 pwa_manifest_path = os.path.join(output_dir, PWA_MANIFEST_FILENAME)
619 with open(pwa_manifest_path, 'w') as pwa_manifest_file: 644 with open(pwa_manifest_path, 'w') as pwa_manifest_file:
620 json.dump(pwa_manifest, pwa_manifest_file, indent=4, sort_keys=True) 645 json.dump(pwa_manifest, pwa_manifest_file, indent=4, sort_keys=True)
621 logging.debug('Wrote `%s` to `%s`.', PWA_MANIFEST_FILENAME, pwa_manifest_path) 646 logging.debug('Wrote `%s` to `%s`.', PWA_MANIFEST_FILENAME, pwa_manifest_path)
622 647
623 # Remove unnecessary files from the output web app. This must be done before 648 # Remove unnecessary files from the output web app. This must be done before
624 # the service worker is generated, or these files will be cached. 649 # the service worker is generated, or these files will be cached.
625 cleanup_output_dir(output_dir) 650 cleanup_output_dir(output_dir)
626 651
627 # Edit the HTML and JS code of the output web app. 652 # Edit the HTML and JS code of the output web app.
628 # This is adding TODOs, injecting tags, etc. - anything that involves editing 653 # This is adding TODOs, injecting tags, etc. - anything that involves editing
629 # user code directly. This must be done before the static code is copied 654 # user code directly. This must be done before the static code is copied
630 # across, or the polyfills will have TODOs added to them. 655 # across, or the polyfills will have TODOs added to them.
631 edit_code(output_dir, required_js_paths, ca_manifest, config) 656 # Order is significant here - always, then dependencies, then polyfills.
657 required_script_paths = (required_always_paths + required_dependency_paths
658 + required_polyfill_paths)
659 edit_code(output_dir, required_script_paths, ca_manifest, config)
632 660
633 # We want the static SW file to be copied in too, so we add it here. 661 # We want the static SW file to be copied in too, so we add it here.
634 # We have to add it after edit_code or it would be included in the HTML, but 662 # We have to add it after edit_code or it would be included in the HTML, but
635 # this is service worker-only code, and shouldn't be included there. 663 # this is service worker-only code, and shouldn't be included there.
636 required_js_paths.append(SW_STATIC_SCRIPT_NAME) 664 required_always_paths.append(SW_STATIC_SCRIPT_NAME)
637 665
638 # Copy static code from Caterpillar into the output web app. 666 # Copy static code from Caterpillar into the output web app.
639 # This must be done before the service worker is generated, or these files 667 # This must be done before the service worker is generated, or these files
640 # will not be cached. 668 # will not be cached.
641 copy_static_code(required_js_paths, output_dir, boilerplate_dir) 669 required_static_paths = required_always_paths + required_polyfill_paths
670 copy_static_code(required_static_paths, output_dir, boilerplate_dir)
642 671
643 # Read in the polyfill manifests and install polyfill dependencies. 672 # Install the polyfill dependencies. This must be done before the service
644 # This must be done after editing code (or the dependencies will also be 673 # worker is generated, or the dependencies won't be cached.
645 # edited) and before the service worker is generated (or the dependencies
646 # won't be cached).
647 polyfill_manifests = polyfill_manifest.load_many(polyfillable)
648 dependencies = [dependency
649 for manifest in polyfill_manifests.values()
650 for dependency in manifest['dependencies']]
651 try: 674 try:
652 install_dependencies(dependencies, output_dir) 675 install_dependencies(dependencies, output_dir)
653 except ValueError as e: 676 except ValueError as e:
654 logging.error(e.message) 677 logging.error(e.message)
655 return 678 return
656 679
657 # Generate and write a service worker. 680 # Generate and write a service worker.
658 add_service_worker(output_dir, ca_manifest, polyfill_paths(polyfillable), 681 required_sw_paths = required_dependency_paths + required_polyfill_paths
682 add_service_worker(output_dir, ca_manifest, required_sw_paths,
659 boilerplate_dir) 683 boilerplate_dir)
660 684
661 logging.info('Conversion complete.') 685 logging.info('Conversion complete.')
662 686
663 687
664 class Formatter(logging.Formatter): 688 class Formatter(logging.Formatter):
665 """Caterpillar logging formatter. 689 """Caterpillar logging formatter.
666 690
667 Adds color to the logged information. 691 Adds color to the logged information.
668 """ 692 """
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
723 if args.mode == 'config': 747 if args.mode == 'config':
724 configuration.generate_and_save(args.output, args.interactive) 748 configuration.generate_and_save(args.output, args.interactive)
725 749
726 elif args.mode == 'convert': 750 elif args.mode == 'convert':
727 config = configuration.load(args.config) 751 config = configuration.load(args.config)
728 convert_app(args.input, args.output, config, args.force) 752 convert_app(args.input, args.output, config, args.force)
729 753
730 754
731 if __name__ == '__main__': 755 if __name__ == '__main__':
732 sys.exit(main()) 756 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