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
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