From e8a062bdd2165c1bbf957028bc8798922880ca81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Rodr=C3=ADguez?= Date: Tue, 28 Feb 2017 23:31:24 +0100 Subject: [PATCH] build: zipper revival (#3319) --- gulpfile.js | 10 +- .../_examples/cli-quickstart/ts/zipper.json | 10 ++ public/docs/_examples/package.json | 28 +++- public/docs/_examples/webpack/ts/zipper.json | 11 ++ public/docs/ts/latest/cli-quickstart.jade | 1 + public/docs/ts/latest/guide/change-log.jade | 4 + public/docs/ts/latest/guide/index.jade | 5 +- public/docs/ts/latest/guide/webpack.jade | 4 +- public/docs/ts/latest/quickstart.jade | 2 +- .../resources/js/directives/live-example.js | 16 +- tools/_example-zipper/Ideas.md | 15 -- tools/_example-zipper/exampleZipper.js | 100 ------------ tools/example-zipper/exampleZipper.js | 146 ++++++++++++++++++ .../test/gulpfile.js | 0 14 files changed, 222 insertions(+), 130 deletions(-) create mode 100644 public/docs/_examples/cli-quickstart/ts/zipper.json create mode 100644 public/docs/_examples/webpack/ts/zipper.json delete mode 100644 tools/_example-zipper/Ideas.md delete mode 100644 tools/_example-zipper/exampleZipper.js create mode 100644 tools/example-zipper/exampleZipper.js rename tools/{_example-zipper => example-zipper}/test/gulpfile.js (100%) diff --git a/gulpfile.js b/gulpfile.js index 04b4bcbe3d..a30abd0fc7 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -46,7 +46,7 @@ var LIVE_EXAMPLES_PATH = path.join(RESOURCES_PATH, 'live-examples'); var STYLES_SOURCE_PATH = path.join(TOOLS_PATH, 'styles-builder/less'); var docShredder = require(path.resolve(TOOLS_PATH, 'doc-shredder/doc-shredder')); -var exampleZipper = require(path.resolve(TOOLS_PATH, '_example-zipper/exampleZipper')); +var ExampleZipper = require(path.resolve(TOOLS_PATH, 'example-zipper/exampleZipper')); var regularPlunker = require(path.resolve(TOOLS_PATH, 'plunker-builder/regularPlunker')); var embeddedPlunker = require(path.resolve(TOOLS_PATH, 'plunker-builder/embeddedPlunker')); var fsUtils = require(path.resolve(TOOLS_PATH, 'fs-utils/fsUtils')); @@ -572,9 +572,7 @@ gulp.task('build-and-serve', ['build-docs'], function (cb) { watchAndSync({localFiles: true}, cb); }); -gulp.task('build-docs', ['build-devguide-docs', 'build-api-docs', 'build-plunkers']); -// Stop zipping examples Feb 28, 2016 -//gulp.task('build-docs', ['build-devguide-docs', 'build-api-docs', 'build-plunkers', '_zip-examples']); +gulp.task('build-docs', ['build-devguide-docs', 'build-api-docs', 'build-plunkers', '_zip-examples']); gulp.task('build-api-docs', ['build-js-api-docs', 'build-ts-api-docs']); @@ -787,8 +785,8 @@ gulp.task('_shred-clean-api', function(cb) { }); gulp.task('_zip-examples', function() { - exampleZipper.zipExamples(_devguideShredOptions.examplesDir, _devguideShredOptions.zipDir); - exampleZipper.zipExamples(_apiShredOptions.examplesDir, _apiShredOptions.zipDir); + new ExampleZipper(_devguideShredOptions.examplesDir, _devguideShredOptions.zipDir); + // exampleZipper.zipExamples(_apiShredOptions.examplesDir, _apiShredOptions.zipDir); }); diff --git a/public/docs/_examples/cli-quickstart/ts/zipper.json b/public/docs/_examples/cli-quickstart/ts/zipper.json new file mode 100644 index 0000000000..c5248e5015 --- /dev/null +++ b/public/docs/_examples/cli-quickstart/ts/zipper.json @@ -0,0 +1,10 @@ +{ + "files":[ + "!**/*.d.ts", + "!**/*.js", + "!**/*.[0-9].*", + ".angular-cli.json", + "protractor.conf.js" + ], + "removeSystemJsConfig": true +} diff --git a/public/docs/_examples/package.json b/public/docs/_examples/package.json index ae481f7415..dd4a3a7f85 100644 --- a/public/docs/_examples/package.json +++ b/public/docs/_examples/package.json @@ -4,9 +4,31 @@ "private": true, "description": "Master package.json, the superset of all dependencies for all of the _example package.json files. See _boilerplate/package.json for example npm scripts.", "scripts": { - "http-server": "http-server", - "protractor": "protractor", - "webdriver:update": "webdriver-manager update --standalone false --gecko false" + "build": "tsc -p src/", + "build:watch": "tsc -p src/ -w", + "build:e2e": "tsc -p e2e/", + "serve": "lite-server -c=bs-config.json", + "prestart": "npm run build", + "start": "concurrently \"npm run build:watch\" \"npm run serve\"", + "protractor": "protractor protractor.config.js", + "pretest": "npm run build", + "test": "concurrently \"npm run build:watch\" \"karma start karma.conf.js\"", + "pretest:once": "npm run build", + "test:once": "karma start karma.conf.js --single-run", + "lint": "tslint ./src/**/*.ts -t verbose", + + "build:upgrade": "tsc", + "serve:upgrade": "http-server", + "build:cli": "ng build --no-progress", + "serve:cli": "http-server dist/", + "build:aot": "ngc -p tsconfig-aot.json && rollup -c rollup-config.js", + "serve:aot": "lite-server -c bs-config.aot.json", + "start:webpack": "webpack-dev-server --inline --progress --port 8080", + "test:webpack": "karma start karma.webpack.conf.js", + "build:webpack": "rimraf dist && webpack --config config/webpack.prod.js --bail", + "build:babel": "babel src -d src --extensions \".es6\" --source-maps", + "copy-dist-files": "node ./copy-dist-files.js", + "i18n": "ng-xi18n" }, "keywords": [], "author": "", diff --git a/public/docs/_examples/webpack/ts/zipper.json b/public/docs/_examples/webpack/ts/zipper.json new file mode 100644 index 0000000000..f429363aa3 --- /dev/null +++ b/public/docs/_examples/webpack/ts/zipper.json @@ -0,0 +1,11 @@ +{ + "files":[ + "!**/*.d.ts", + "!**/*.js", + "!**/*.[0-9].*", + "config/**/*", + "webpack.config.js", + "karma.webpack.conf.js" + ], + "removeSystemJsConfig": true +} diff --git a/public/docs/ts/latest/cli-quickstart.jade b/public/docs/ts/latest/cli-quickstart.jade index b5cc2e11a3..f1df0cb240 100644 --- a/public/docs/ts/latest/cli-quickstart.jade +++ b/public/docs/ts/latest/cli-quickstart.jade @@ -23,6 +23,7 @@ include _util-fns 3. [Serve](#serve) the application 4. [Edit](#first-component) the application + And you can also download the example. .l-main-section h2#devenv Step 1. Set up the Development Environment diff --git a/public/docs/ts/latest/guide/change-log.jade b/public/docs/ts/latest/guide/change-log.jade index 23cacba033..60ca5240cf 100644 --- a/public/docs/ts/latest/guide/change-log.jade +++ b/public/docs/ts/latest/guide/change-log.jade @@ -5,6 +5,10 @@ block includes The Angular documentation is a living document with continuous improvements. This log calls attention to recent significant changes. + ## NEW: Downloadable examples for each guide (2017-02-28) + Now you can download the sample code for any guide and run it locally. + Look for the new download links next to the "live example" links. + ## Template Syntax/Structural Directives: refreshed (2017-02-06) The [_Template-Syntax_](template-syntax.html) and [_Structural Directives_](structural-directives.html) guides were significantly revised for clarity, accuracy, and current recommended practices. diff --git a/public/docs/ts/latest/guide/index.jade b/public/docs/ts/latest/guide/index.jade index e26e153031..cd0c27be9b 100644 --- a/public/docs/ts/latest/guide/index.jade +++ b/public/docs/ts/latest/guide/index.jade @@ -72,11 +72,14 @@ table(width="100%") You can reuse these snippets in your applications. Look for a link to a running version of that sample, often near the top of the page, - such as this from the [Architecture](architecture.html) page. + such as this from the [Architecture](architecture.html) page. The link launches a browser-based, code editor where you can inspect, modify, save, and download the code. + Alternatively, you can run the example locally, next to those `live-example` links you have a download link. + Just download, unzip, run `npm install` to install the dependencies and run it with `npm start`. + ## Reference pages * The [Cheat Sheet](cheatsheet.html) lists Angular syntax for common scenarios. diff --git a/public/docs/ts/latest/guide/webpack.jade b/public/docs/ts/latest/guide/webpack.jade index 59832d6e0a..262b5e8a1a 100644 --- a/public/docs/ts/latest/guide/webpack.jade +++ b/public/docs/ts/latest/guide/webpack.jade @@ -32,7 +32,9 @@ style. [Trying it out](#try) [Conclusions](#conclusions) - + + You can also download the final result. + .l-main-section :marked diff --git a/public/docs/ts/latest/quickstart.jade b/public/docs/ts/latest/quickstart.jade index e61dd9beb6..480c62a8c8 100644 --- a/public/docs/ts/latest/quickstart.jade +++ b/public/docs/ts/latest/quickstart.jade @@ -10,7 +10,7 @@ block includes block qs-src-online-and-local .l-sub-section :marked - Try this **QuickStart example on Plunker** without installing anything. + Try this **QuickStart example on Plunker** without installing anything. Try it locally with the [***QuickStart seed***](guide/setup.html "Setup for local development with the QuickStart seed") and prepare for development of a real Angular application. diff --git a/public/resources/js/directives/live-example.js b/public/resources/js/directives/live-example.js index 6d22812ae9..39c5ae35ef 100644 --- a/public/resources/js/directives/live-example.js +++ b/public/resources/js/directives/live-example.js @@ -56,11 +56,12 @@ angularIO.directive('liveExample', ['$location', function ($location) { function span(text) { return '' + text + ''; } - function embeddedTemplate(src, img) { + function embeddedTemplate(src, img, zipHref) { return '
' + '' + '
' + - 'plunker'; + 'plunker' + + '

You can also download this example.

'; } return { @@ -72,24 +73,29 @@ angularIO.directive('liveExample', ['$location', function ($location) { if (attrs['title'] == undefined) { tElement[0].setAttribute('title', text); } // set default title (tooltip) var ex = attrs.name || NgIoUtil.getExampleName($location); var embedded = attrs.hasOwnProperty('embedded'); + var noDownload = attrs.hasOwnProperty('nodownload') || attrs.hasOwnProperty('noDownload'); var flatStyle = attrs.hasOwnProperty('flatstyle') || attrs.hasOwnProperty('flatStyle'); var embeddedStyle = embedded || attrs.hasOwnProperty('embeddedstyle') || attrs.hasOwnProperty('embeddedStyle'); var plnkr = (embeddedStyle || !flatStyle) ? 'eplnkr' : 'plnkr'; + var zipHref = ex; var imageBase = '/resources/images/'; var defaultImg = 'plunker/placeholder.png'; if (attrs.plnkr) { plnkr = attrs.plnkr + '.' + plnkr; + zipHref = attrs.plnkr + '.' + zipHref; } var isForDart = attrs.lang === 'dart' || NgIoUtil.isDoc($location, 'dart'); var isForJs = attrs.lang === 'js' || NgIoUtil.isDoc($location, 'js'); var exLang = isForDart ? 'dart' : isForJs ? 'js' : 'ts'; + zipHref = '/resources/zips/' + ex + '/' + zipHref + '.zip'; + if (embedded && !isForDart) { href = '/resources/live-examples/' + ex + '/' + exLang + '/' + plnkr + '.html'; img = imageBase + (attrs.img || defaultImg); - template = embeddedTemplate(href, img); + template = embeddedTemplate(href, img, zipHref); } else { var href = isForDart ? 'http://angular-examples.github.io/' + ex @@ -98,6 +104,10 @@ angularIO.directive('liveExample', ['$location', function ($location) { // Link to live example. var template = a(text, { href: href, target: '_blank' }); + if (!noDownload) { + template += ' / ' + a('downloadable example', { href: zipHref, target: '_blank' }); + } + // The hosted example and sources are in different locations for Dart. // Also show link to sources for Dart, unless noSource is specified. if (isForDart && !attrs.hasOwnProperty('nosource')) { diff --git a/tools/_example-zipper/Ideas.md b/tools/_example-zipper/Ideas.md deleted file mode 100644 index ee41ef8094..0000000000 --- a/tools/_example-zipper/Ideas.md +++ /dev/null @@ -1,15 +0,0 @@ -Summary: - - 1) if we discover a 'zipconfig.json' file or an 'xxx.zipconfig.json' file with the following structure - - { - "zipRegion": "zip", - "files": [ "foo.js", "**/**/.css", "!xxx.css"] - } - - where "zipRegion" is optional. - - Then we zip up all of the files specified, cleaning and removing extra doc tags. If the specified 'zipRegion' - is discovered in any file then we only zip that region. Otherwise we zip the entire file. - - \ No newline at end of file diff --git a/tools/_example-zipper/exampleZipper.js b/tools/_example-zipper/exampleZipper.js deleted file mode 100644 index 303933e4e0..0000000000 --- a/tools/_example-zipper/exampleZipper.js +++ /dev/null @@ -1,100 +0,0 @@ -// Canonical path provides a consistent path (i.e. always forward slashes) across different OSes -var path = require('canonical-path'); -var jsonfile = require('jsonfile'); -var assert = require('assert-plus'); -// adm-zip does not work properly on Windows -// var Zip = require('adm-zip'); -var archiver = require('archiver'); - -var fs = require('fs'); -var mkdirp = require('mkdirp'); -var globby = require('globby'); - -var regionExtractor = require('../doc-shredder/regionExtractor'); - - -// globs is an array of globs -function zipExamples(sourceDirName, outputDirName) { - var gpath = path.join(sourceDirName, '**/*zipconfig.json'); - var configFileNames = globby.sync([gpath], { ignore: ['**/node_modules/**'] }); - configFileNames.forEach(function(configFileName) { - zipExample(configFileName, sourceDirName, outputDirName); - }); -} - -function zipExample(configFileName, sourceDirName, outputDirName) { - var json = jsonfile.readFileSync(configFileName); - var fileGlobs = json.files; - var zipRegionName = json.zipRegion; - - // assert that fileGlobs is an array - assert.arrayOfString(fileGlobs, 'files property should be an array of strings'); - - // backup two from the relative configFileName - var relativeDirName = path.dirname(path.dirname(path.relative(sourceDirName, configFileName))); - var configDirName = path.dirname(configFileName); - // use the dir name of the folder containing the config file as the base file name of the zip file - var baseFileName = path.basename(configDirName); - // check if there is a prefix name before zipconfig.json - if (configFileName.indexOf('.zipconfig.json') >= 0) { - // if so use it as a suffix to the baseFileName - var extraName = path.basename(configFileName, '.zipconfig.json'); - baseFileName = baseFileName + "." + extraName; - } - - var outputFileName = path.join(outputDirName, relativeDirName, baseFileName + ".zip"); - // old code - // var fileNames = globule.find(fileGlobs, { srcBase: configDirName, prefixBase: configDirName }); - fileGlobs = fileGlobs.map(function(fileGlob) { - return path.join(configDirName, fileGlob); - }); - var fileNames = globby.sync(fileGlobs, { ignore: ["**/node_modules/**"] }); - - var zip = createZipArchive(outputFileName); - fileNames.forEach(function(fileName) { - var relativePath = path.relative(configDirName, fileName); - var content = fs.readFileSync(fileName, 'utf8'); - var extn = path.extname(fileName).substr(1); - // if we don't need to clean up the file then we can do the following. - // zip.append(fs.createReadStream(fileName), { name: relativePath }); - var output; - // if no zipRegion or zipRegion is not in content then just clean the content - if (zipRegionName != null) { - output = regionExtractor.getRegionDoc(content, extn, zipRegionName); - } - if (!output) { - output = regionExtractor.removeDocTags(content, extn); - } - - zip.append(output, { name: relativePath } ) - }); - - zip.finalize(); -} - -function createZipArchive(zipFileName) { - var dirName = path.dirname(zipFileName); - // insure that the folder exists. - if (!fs.existsSync(dirName)) { - mkdirp.sync(dirName); - } - var output = fs.createWriteStream(zipFileName); - var archive = archiver('zip'); - - output.on('close', function () { - console.log('zip created: ' + zipFileName + ' (' + archive.pointer() + ' total bytes)'); - }); - - archive.on('error', function (err) { - throw err; - }); - - archive.pipe(output); - return archive; -} - - -module.exports = { - zipExamples: zipExamples, - zipExample: zipExample -}; \ No newline at end of file diff --git a/tools/example-zipper/exampleZipper.js b/tools/example-zipper/exampleZipper.js new file mode 100644 index 0000000000..a92e45983a --- /dev/null +++ b/tools/example-zipper/exampleZipper.js @@ -0,0 +1,146 @@ +// Canonical path provides a consistent path (i.e. always forward slashes) across different OSes +var path = require('canonical-path'); +var jsonfile = require('jsonfile'); +var assert = require('assert-plus'); +// adm-zip does not work properly on Windows +// var Zip = require('adm-zip'); +var archiver = require('archiver'); + +var fs = require('fs'); +var mkdirp = require('mkdirp'); +var globby = require('globby'); + +var regionExtractor = require('../doc-shredder/regionExtractor'); + +class ExampleZipper { + constructor(sourceDirName, outputDirName) { + let gpathPlnkr = path.join(sourceDirName, '**/*plnkr.json'); + let gpathZipper = path.join(sourceDirName, '**/zipper.json'); + let configFileNames = globby.sync([gpathPlnkr, gpathZipper], { ignore: ['**/node_modules/**'] }); + // we only need typescript examples + configFileNames = configFileNames.filter((configFileName) => { + return configFileName.indexOf('ts') != -1; + }); + configFileNames.forEach((configFileName) => { + this._zipExample(configFileName, sourceDirName, outputDirName); + }); + } + + _changeTypeRoots(tsconfig) { + return tsconfig.replace('../../../', '../'); + } + + _createZipArchive(zipFileName) { + let dirName = path.dirname(zipFileName); + // ensure that the folder exists. + if (!fs.existsSync(dirName)) { + mkdirp.sync(dirName); + } + let output = fs.createWriteStream(zipFileName); + let archive = archiver('zip'); + + output.on('close', function () { + console.log('zip created: ' + zipFileName + ' (' + archive.pointer() + ' total bytes)'); + }); + + archive.on('error', function (err) { + throw err; + }); + + archive.pipe(output); + return archive; + } + + _zipExample(configFileName, sourceDirName, outputDirName) { + let json = JSON.parse(fs.readFileSync(configFileName, 'utf-8')); + const basePath = json.basePath || ''; + const jsonFileName = configFileName.replace(/^.*[\\\/]/, ''); + const relativeDirName = path.dirname(path.dirname(path.relative(sourceDirName, configFileName))); + const exampleDirName = path.dirname(configFileName); + const examplesPackageJson = 'public/docs/_examples/package.json'; + const examplesSystemjsConfig = 'public/docs/_examples/_boilerplate/src/systemjs.config.js'; + const exampleTsconfig = 'public/docs/_examples/_boilerplate/src/tsconfig.json'; + let exampleZipName = jsonFileName.replace(/(plnkr|zipper).json/, relativeDirName); + const outputFileName = path.join(outputDirName, relativeDirName, exampleZipName + '.zip'); + let defaultIncludes = ['**/*.ts', '**/*.js', '**/*.css', '**/*.html', '**/*.md', '**/*.json', '**/*.png']; + let alwaysIncludes = ['bs-config.json', 'tslint.json', 'karma-test-shim.js', 'karma.conf.js', 'src/testing/**/*']; + var defaultExcludes = [ + '!**/bs-config.e2e.json', + '!**/*plnkr.*', + '!**/*zipper.*', + '!**/systemjs.config.js', + '!**/npm-debug.log', + '!**/package.json', + '!**/example-config.json', + '!**/wallaby.js', + '!**/tsconfig.json', + '!**/package.webpack.json', + // AoT related files + '!**/aot/**/*.*', + '!**/*-aot.*' + ]; + + if (json.files) { + if (json.files.length > 0) { + json.files = json.files.map(file => { + if (file.startsWith('!')) { + if (file.startsWith('!**')) { + return file; + } + + return '!' + basePath + file.substr(1); + } + + return basePath + file; + }); + + if (json.files[0].substr(0, 1) === '!') { + json.files = defaultIncludes.concat(json.files); + } + } + } else { + json.files = defaultIncludes; + } + + json.files = json.files.concat(alwaysIncludes); + + let gpaths = json.files.map((fileName) => { + fileName = fileName.trim(); + if (fileName.substr(0, 1) === '!') { + return '!' + path.join(exampleDirName, fileName.substr(1)); + } else { + return path.join(exampleDirName, fileName); + } + }); + + Array.prototype.push.apply(gpaths, defaultExcludes); + + let fileNames = globby.sync(gpaths, { ignore: ['**/node_modules/**']}); + + let zip = this._createZipArchive(outputFileName); + fileNames.forEach((fileName) => { + let relativePath = path.relative(exampleDirName, fileName); + let content = fs.readFileSync(fileName, 'utf8'); + let extn = path.extname(fileName).substr(1); + // if we don't need to clean up the file then we can do the following. + // zip.append(fs.createReadStream(fileName), { name: relativePath }); + let output = regionExtractor.removeDocTags(content, extn); + + zip.append(output, { name: relativePath } ) + }); + + // we need the package.json from _examples root, not the _boilerplate one + zip.append(fs.readFileSync(examplesPackageJson, 'utf8'), { name: 'package.json' }); + // also a systemjs config + if (!json.removeSystemJsConfig) { + zip.append(fs.readFileSync(examplesSystemjsConfig, 'utf8'), { name: 'src/systemjs.config.js' }); + } + // a modified tsconfig + let tsconfig = fs.readFileSync(exampleTsconfig, 'utf8'); + zip.append(this._changeTypeRoots(tsconfig), {name: 'src/tsconfig.json'}); + + zip.finalize(); + } +} + +module.exports = ExampleZipper; diff --git a/tools/_example-zipper/test/gulpfile.js b/tools/example-zipper/test/gulpfile.js similarity index 100% rename from tools/_example-zipper/test/gulpfile.js rename to tools/example-zipper/test/gulpfile.js