Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f637dc76bd | |||
| e8299e0485 | |||
| 7989a5e9f9 | |||
| bde5506b10 | |||
| 6118a3c829 | |||
| c5e5cad579 | |||
| 1c75903eaf | |||
| ab920cc1ce | |||
| 520e1d5f08 | |||
| 02423780a9 | |||
| be92d2f254 | |||
| 443a613b3a | |||
| 83ee5908f2 | |||
| 8dc3d2517b | |||
| 668c4fb878 | |||
| c65c6cce3f | |||
| 3d3796c1e9 | |||
| 075b354a3b | |||
| 30dbf04781 | |||
| e3a54485db | |||
| 8e23642ea2 | |||
| 88ac06875f | |||
| c68bc08a0d | |||
| 6cfbc2653a | |||
| b65135acef |
@@ -33,7 +33,7 @@ jobs:
|
||||
matrix:
|
||||
operating-system: ['ubuntu-latest', 'windows-latest']
|
||||
# https://nodejs.org/en/about/releases/
|
||||
node-version: ['10', '12', '14', '16', '17']
|
||||
node-version: ['10', '12', '14', '16', '18', '20']
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
|
||||
- name: Test (Integration)
|
||||
# https://github.com/webpack/webpack/issues/14532
|
||||
if: ${{ matrix.node-version != '17' }}
|
||||
if: ${{ matrix.node-version != '18' && matrix.node-version != '20' }}
|
||||
run: |
|
||||
cd ./tests/integration/rollup-test && ./test.sh && cd -
|
||||
cd ./tests/integration/webpack-babel-test && ./test.sh && cd -
|
||||
@@ -62,7 +62,7 @@ jobs:
|
||||
|
||||
browser:
|
||||
name: Test (Browser)
|
||||
runs-on: 'ubuntu-latest'
|
||||
runs-on: 'ubuntu-20.04'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
publish-aws-s3:
|
||||
name: Publish to AWS S3
|
||||
runs-on: 'ubuntu-latest'
|
||||
environment: 'builds.handlebarsjs.com.s3.amazonaws.com'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '16'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Publish
|
||||
run: |
|
||||
git config --global user.email "release@handlebarsjs.com"
|
||||
git config --global user.name "handlebars-lang"
|
||||
npm run publish:aws
|
||||
env:
|
||||
S3_BUCKET_NAME: "builds.handlebarsjs.com"
|
||||
S3_ACCESS_KEY_ID: ${{ secrets.S3_ACCESS_KEY_ID }}
|
||||
S3_SECRET_ACCESS_KEY: ${{ secrets.S3_SECRET_ACCESS_KEY }}
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
[submodule "spec/mustache"]
|
||||
path = spec/mustache
|
||||
url = git://github.com/mustache/spec.git
|
||||
url = https://github.com/mustache/spec.git
|
||||
|
||||
+76
-9
@@ -86,33 +86,100 @@ You can use the following scripts to make sure that the CI job does not fail:
|
||||
- **npm run lint** will run `eslint` and fail on warnings
|
||||
- **npm run format** will run `prettier` on all files
|
||||
- **npm run check-before-pull-request** will perform all most checks that our CI job does in its build-job, excluding the "integration-test".
|
||||
- **npm run integration-test** will run integration tests (using old NodeJS versions and integrations with webpack, babel and so on)
|
||||
- **npm run test:integration** will run integration tests (using old NodeJS versions and integrations with webpack, babel and so on)
|
||||
These tests only work on a Linux-machine with `nvm` installed (for running tests in multiple versions of NodeJS).
|
||||
|
||||
## Releasing the latest version
|
||||
|
||||
Before attempting the release Handlebars, please make sure that you have the following authorizations:
|
||||
|
||||
- Push-access to `handlebars-lang/handlebars.js`
|
||||
- Publishing rights on npmjs.com for the `handlebars` package
|
||||
- Publishing rights on gemfury for the `handlebars-source` package
|
||||
- Push-access to the repo for legacy package managers: `components/handlebars`
|
||||
- Push-access to the production-repo of the handlebars site: `handlebars-lang/handlebarsjs.com-github-pages`
|
||||
- Push-access to [handlebars-lang/handlebars.js](https://github.com/handlebars-lang/handlebars.js/)
|
||||
- Publishing rights on npmjs.com for the [handlebars](https://www.npmjs.com/package/handlebars) package
|
||||
- Publishing rights on rubygems for the [handlebars-source](https://rubygems.org/gems/handlebars-source) package
|
||||
- Push-access to the repo for legacy package managers: [components/handlebars.js](https://github.com/components/handlebars.js)
|
||||
- Push-access to the production-repo of the handlebars site: [handlebars-lang/docs](https://github.com/handlebars-lang/docs)
|
||||
|
||||
_When releasing a previous version of Handlebars, please look into the CONTRIBUNG.md in the corresponding branch._
|
||||
|
||||
A full release may be completed with the following:
|
||||
A full release via Docker may be completed with the following:
|
||||
|
||||
1. Create a `Dockerfile` in this folder for releasing
|
||||
```Dockerfile
|
||||
FROM node:10-slim
|
||||
|
||||
ENV EDITOR=vim
|
||||
|
||||
# Update stretch repositories
|
||||
RUN sed -i -e 's/deb.debian.org/archive.debian.org/g' \
|
||||
-e 's|security.debian.org|archive.debian.org/|g' \
|
||||
-e '/stretch-updates/d' /etc/apt/sources.list
|
||||
|
||||
# Install release dependencies
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y git vim
|
||||
|
||||
# Work around deprecated npm dependency install via unauthenticated git-protocol:
|
||||
# https://github.com/kpdecker/generator-release/blob/87aab9b84c9f083635c3fcc822f18acce1f48736/package.json#L31
|
||||
RUN git config --system url."https://github.com/".insteadOf git://github.com/
|
||||
|
||||
# Configure git
|
||||
RUN git config --system user.email "release@handlebarsjs.com"
|
||||
RUN git config --system user.name "handlebars-lang"
|
||||
|
||||
RUN mkdir /home/node/.config
|
||||
RUN mkdir /home/node/.ssh
|
||||
RUN mkdir /home/node/tmp
|
||||
|
||||
# Generate config for yo generator-release:
|
||||
# https://github.com/kpdecker/generator-release#example
|
||||
# You have to add a valid GitHub OAuth token!
|
||||
RUN echo "module.exports = {\n auth: 'oauth',\n token: 'GitHub OAuth token'\n};" > /home/node/.config/generator-release
|
||||
RUN chown -R node:node /home/node/.config
|
||||
|
||||
# Add the generated key to GitHub: https://github.com/settings/keys
|
||||
RUN ssh-keygen -q -t ed25519 -N '' -f /home/node/.ssh/id_ed25519 -C "release@handlebarsjs.com"
|
||||
RUN chmod 0600 /home/node/.ssh/id_ed25519*
|
||||
RUN chown node:node /home/node/.ssh/id_ed25519*
|
||||
```
|
||||
2. Build and run the Docker image
|
||||
```bash
|
||||
docker build --tag handlebars:release .
|
||||
docker run --rm --interactive --tty \
|
||||
--volume $PWD:/app \
|
||||
--workdir /app \
|
||||
--user $(id -u):$(id -g) \
|
||||
--env NPM_CONFIG_PREFIX=/home/node/.npm-global \
|
||||
handlebars:release bash -c 'export PATH=$PATH:/home/node/.npm-global/bin; bash'
|
||||
```
|
||||
* Add SSH key to GitHub: `cat /home/node/.ssh/id_ed25519.pub` (https://github.com/settings/keys)
|
||||
* Add GitHub API token: `vi /home/node/.config/generator-release`
|
||||
* Execute the following steps:
|
||||
```bash
|
||||
npm ci
|
||||
npx grunt
|
||||
npm install -g yo@1 grunt@1 generator-release
|
||||
npm run release
|
||||
yo release
|
||||
npm login
|
||||
npm publish
|
||||
|
||||
yo release:publish components handlebars.js dist/components/
|
||||
```
|
||||
6. Publish Ruby `handlebars-source` gem:
|
||||
```bash
|
||||
docker run --rm --interactive --tty \
|
||||
--volume $PWD:/app \
|
||||
--workdir /app \
|
||||
--user $(id -u):$(id -g) \
|
||||
ruby:3.2-slim bash
|
||||
```
|
||||
* Execute the following steps:
|
||||
```bash
|
||||
cd dist/components/
|
||||
gem build handlebars-source.gemspec
|
||||
gem push handlebars-source-*.gem
|
||||
```
|
||||
|
||||
### After the release
|
||||
|
||||
After the release, you should check that all places have really been updated. Especially verify that the `latest`-tags
|
||||
in those places still point to the latest version
|
||||
|
||||
|
||||
+5
-7
@@ -166,11 +166,9 @@ module.exports = function(grunt) {
|
||||
}
|
||||
},
|
||||
|
||||
bgShell: {
|
||||
shell: {
|
||||
integrationTests: {
|
||||
cmd: './tests/integration/run-integration-tests.sh',
|
||||
bg: false,
|
||||
fail: true
|
||||
command: './tests/integration/run-integration-tests.sh'
|
||||
}
|
||||
},
|
||||
|
||||
@@ -195,7 +193,7 @@ module.exports = function(grunt) {
|
||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||
grunt.loadNpmTasks('grunt-contrib-watch');
|
||||
grunt.loadNpmTasks('grunt-babel');
|
||||
grunt.loadNpmTasks('grunt-bg-shell');
|
||||
grunt.loadNpmTasks('grunt-shell');
|
||||
grunt.loadNpmTasks('grunt-webpack');
|
||||
|
||||
grunt.task.loadTasks('tasks');
|
||||
@@ -214,7 +212,7 @@ module.exports = function(grunt) {
|
||||
// Requires secret properties from .travis.yaml
|
||||
grunt.registerTask('extensive-tests-and-publish-to-aws', [
|
||||
'default',
|
||||
'bgShell:integrationTests',
|
||||
'shell:integrationTests',
|
||||
'metrics',
|
||||
'publish-to-aws'
|
||||
]);
|
||||
@@ -234,6 +232,6 @@ module.exports = function(grunt) {
|
||||
);
|
||||
grunt.registerTask('integration-tests', [
|
||||
'default',
|
||||
'bgShell:integrationTests'
|
||||
'shell:integrationTests'
|
||||
]);
|
||||
};
|
||||
|
||||
+7
-7
@@ -1,18 +1,18 @@
|
||||
[](https://github.com/handlebars-lang/handlebars.js/actions)
|
||||
[](https://github.com/handlebars-lang/handlebars.js/actions/workflows/ci.yml)
|
||||
[](https://www.jsdelivr.com/package/npm/handlebars)
|
||||
[](https://bundlephobia.com/package/handlebars)
|
||||
[](https://www.npmjs.com/package/handlebars)
|
||||
[](https://www.npmjs.com/package/handlebars)
|
||||
[](https://bundlephobia.com/package/handlebars)
|
||||
[](https://packagephobia.com/result?p=handlebars)
|
||||
|
||||
Handlebars.js
|
||||
=============
|
||||
|
||||
Handlebars.js is an extension to the [Mustache templating
|
||||
language](https://mustache.github.io/) created by Chris Wanstrath.
|
||||
Handlebars.js and Mustache are both logicless templating languages that
|
||||
keep the view and the code separated like we all know they should be.
|
||||
Handlebars provides the power necessary to let you build **semantic templates** effectively with no frustration.
|
||||
Handlebars is largely compatible with Mustache templates. In most cases it is possible to swap out Mustache with Handlebars and continue using your current templates.
|
||||
|
||||
Checkout the official Handlebars docs site at
|
||||
[https://handlebarsjs.com/](https://handlebarsjs.com) and the live demo at [http://tryhandlebarsjs.com/](http://tryhandlebarsjs.com/).
|
||||
[handlebarsjs.com](https://handlebarsjs.com) and try our [live demo](https://handlebarsjs.com/playground.html).
|
||||
|
||||
Installing
|
||||
----------
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "handlebars",
|
||||
"version": "4.7.7",
|
||||
"version": "4.7.8",
|
||||
"main": "handlebars.js",
|
||||
"license": "MIT",
|
||||
"dependencies": {}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<package>
|
||||
<metadata>
|
||||
<id>handlebars.js</id>
|
||||
<version>4.7.7</version>
|
||||
<version>4.7.8</version>
|
||||
<authors>handlebars.js Authors</authors>
|
||||
<licenseUrl>https://github.com/handlebars-lang/handlebars.js/blob/master/LICENSE</licenseUrl>
|
||||
<projectUrl>https://github.com/handlebars-lang/handlebars.js/</projectUrl>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "handlebars",
|
||||
"version": "4.7.7",
|
||||
"version": "4.7.8",
|
||||
"license": "MIT",
|
||||
"jspm": {
|
||||
"main": "handlebars",
|
||||
|
||||
+4
-2
@@ -1,6 +1,8 @@
|
||||
module.exports = {
|
||||
env: {
|
||||
// Handlebars should run natively in the browser
|
||||
node: false
|
||||
// Handlebars should not use node or browser-specific APIs
|
||||
'shared-node-browser': true,
|
||||
node: false,
|
||||
browser: false
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@ import { registerDefaultDecorators } from './decorators';
|
||||
import logger from './logger';
|
||||
import { resetLoggedProperties } from './internal/proto-access';
|
||||
|
||||
export const VERSION = '4.7.7';
|
||||
export const VERSION = '4.7.8';
|
||||
export const COMPILER_REVISION = 8;
|
||||
export const LAST_COMPATIBLE_COMPILER_REVISION = 7;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* global define */
|
||||
/* global define, require */
|
||||
import { isArray } from '../utils';
|
||||
|
||||
let SourceNode;
|
||||
@@ -8,7 +8,7 @@ try {
|
||||
if (typeof define !== 'function' || !define.amd) {
|
||||
// We don't support this in AMD environments. For these environments, we assume that
|
||||
// they are running on the browser and thus have no need for the source-map library.
|
||||
let SourceMap = require('source-map'); // eslint-disable-line no-undef
|
||||
let SourceMap = require('source-map');
|
||||
SourceNode = SourceMap.SourceNode;
|
||||
}
|
||||
} catch (err) {
|
||||
|
||||
@@ -537,7 +537,9 @@ JavaScriptCompiler.prototype = {
|
||||
|
||||
resolvePath: function(type, parts, i, falsy, strict) {
|
||||
if (this.options.strict || this.options.assumeObjects) {
|
||||
this.push(strictLookup(this.options.strict && strict, this, parts, type));
|
||||
this.push(
|
||||
strictLookup(this.options.strict && strict, this, parts, i, type)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1261,9 +1263,8 @@ JavaScriptCompiler.isValidJavaScriptVariableName = function(name) {
|
||||
);
|
||||
};
|
||||
|
||||
function strictLookup(requireTerminal, compiler, parts, type) {
|
||||
function strictLookup(requireTerminal, compiler, parts, i, type) {
|
||||
let stack = compiler.popStack(),
|
||||
i = 0,
|
||||
len = parts.length;
|
||||
if (requireTerminal) {
|
||||
len--;
|
||||
|
||||
@@ -1,11 +1,22 @@
|
||||
/* global globalThis */
|
||||
export default function(Handlebars) {
|
||||
/* istanbul ignore next */
|
||||
let root = typeof global !== 'undefined' ? global : window, // eslint-disable-line no-undef
|
||||
$Handlebars = root.Handlebars;
|
||||
// https://mathiasbynens.be/notes/globalthis
|
||||
(function() {
|
||||
if (typeof globalThis === 'object') return;
|
||||
Object.prototype.__defineGetter__('__magic__', function() {
|
||||
return this;
|
||||
});
|
||||
__magic__.globalThis = __magic__; // eslint-disable-line no-undef
|
||||
delete Object.prototype.__magic__;
|
||||
})();
|
||||
|
||||
const $Handlebars = globalThis.Handlebars;
|
||||
|
||||
/* istanbul ignore next */
|
||||
Handlebars.noConflict = function() {
|
||||
if (root.Handlebars === Handlebars) {
|
||||
root.Handlebars = $Handlebars;
|
||||
if (globalThis.Handlebars === Handlebars) {
|
||||
globalThis.Handlebars = $Handlebars;
|
||||
}
|
||||
return Handlebars;
|
||||
};
|
||||
|
||||
Generated
+3772
-229
File diff suppressed because it is too large
Load Diff
+6
-8
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "handlebars",
|
||||
"barename": "handlebars",
|
||||
"version": "4.7.7",
|
||||
"version": "4.7.8",
|
||||
"description": "Handlebars provides the power necessary to let you build semantic templates effectively with no frustration",
|
||||
"homepage": "https://www.handlebarsjs.com/",
|
||||
"homepage": "https://handlebarsjs.com/",
|
||||
"keywords": [
|
||||
"handlebars",
|
||||
"mustache",
|
||||
@@ -30,8 +30,8 @@
|
||||
"uglify-js": "^3.1.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aws-sdk/client-s3": "^3.385.0",
|
||||
"@playwright/test": "^1.17.1",
|
||||
"aws-sdk": "^2.1.49",
|
||||
"babel-loader": "^5.0.0",
|
||||
"babel-runtime": "^5.1.10",
|
||||
"benchmark": "~1.0",
|
||||
@@ -49,7 +49,6 @@
|
||||
"fs-extra": "^8.1.0",
|
||||
"grunt": "^1.0.4",
|
||||
"grunt-babel": "^5.0.0",
|
||||
"grunt-bg-shell": "^2.3.3",
|
||||
"grunt-cli": "^1",
|
||||
"grunt-contrib-clean": "^1",
|
||||
"grunt-contrib-concat": "^1",
|
||||
@@ -58,6 +57,7 @@
|
||||
"grunt-contrib-requirejs": "^1",
|
||||
"grunt-contrib-uglify": "^1",
|
||||
"grunt-contrib-watch": "^1.1.0",
|
||||
"grunt-shell": "^4.0.0",
|
||||
"grunt-webpack": "^1.0.8",
|
||||
"husky": "^3.1.0",
|
||||
"jison": "~0.3.0",
|
||||
@@ -76,15 +76,13 @@
|
||||
},
|
||||
"main": "lib/index.js",
|
||||
"types": "types/index.d.ts",
|
||||
"browser": {
|
||||
".": "./dist/cjs/handlebars.js",
|
||||
"./runtime": "./dist/cjs/handlebars.runtime.js"
|
||||
},
|
||||
"browser": "./dist/cjs/handlebars.js",
|
||||
"bin": {
|
||||
"handlebars": "bin/handlebars"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "grunt build",
|
||||
"release": "npm run build && grunt release",
|
||||
"format": "prettier --write '**/*.js' && eslint --fix .",
|
||||
"lint": "npm run lint:eslint && npm run lint:prettier && npm run lint:types",
|
||||
"lint:eslint": "eslint --max-warnings 0 .",
|
||||
|
||||
+12
-1
@@ -2,7 +2,18 @@
|
||||
|
||||
## Development
|
||||
|
||||
[Commits](https://github.com/handlebars-lang/handlebars.js/compare/v4.7.7...master)
|
||||
[Commits](https://github.com/handlebars-lang/handlebars.js/compare/v4.7.8...master)
|
||||
|
||||
## v4.7.8 - July 27th, 2023
|
||||
|
||||
- Make library compatible with workers (#1894) - 3d3796c
|
||||
- Don't rely on Node.js global object (#1776) - 2954e7e
|
||||
- Fix compiling of each block params in strict mode (#1855) - 30dbf04
|
||||
- Fix rollup warning when importing Handlebars as ESM - 03d387b
|
||||
- Fix bundler issue with webpack 5 (#1862) - c6c6bbb
|
||||
- Use https instead of git for mustache submodule - 88ac068
|
||||
|
||||
[Commits](https://github.com/handlebars-lang/handlebars.js/compare/v4.7.7...v4.7.8)
|
||||
|
||||
## v4.7.7 - February 15th, 2021
|
||||
|
||||
|
||||
@@ -324,6 +324,15 @@ describe('builtin helpers', function() {
|
||||
);
|
||||
});
|
||||
|
||||
it('each with block params and strict compilation', function() {
|
||||
expectTemplate(
|
||||
'{{#each goodbyes as |value index|}}{{index}}. {{value.text}}!{{/each}}'
|
||||
)
|
||||
.withCompileOptions({ strict: true })
|
||||
.withInput({ goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }] })
|
||||
.toCompileTo('0. goodbye!1. Goodbye!');
|
||||
});
|
||||
|
||||
it('each object with @index', function() {
|
||||
expectTemplate(
|
||||
'{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!'
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport"
|
||||
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<style>
|
||||
table {
|
||||
column-gap: 1rem;
|
||||
}
|
||||
thead {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background: #efefef;
|
||||
}
|
||||
|
||||
th:not(:last-child), td:not(:last-child) {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
<title>Handlebars.js Builds</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Handlebars.js builds</h1>
|
||||
<p>See <a href="https://handlebarsjs.com">https://handlebarsjs.com</a> for documentation.</p>
|
||||
<p>Machine-readable version: <a href="{{jsonListUrl}}">{{jsonListUrl}}</a></p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-col="key"><a href="#" onclick="return toggleSort('key')">Name</a></th>
|
||||
<th data-col="size"><a href="#" onclick="return toggleSort('size')">Size</a></th>
|
||||
<th data-col="lastModified"><a href="#" onclick="return toggleSort('lastModified')">Last-Modified</a></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="files">
|
||||
{{#each fileList as | file |}}
|
||||
<tr>
|
||||
<td data-col="key"><a href="{{file.key}}">{{key}}</a></td>
|
||||
<td data-col="size">{{file.size}}</td>
|
||||
<td data-col="lastModified">{{file.lastModified}}</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
<script type="application/javascript">
|
||||
const files = {{{json fileList}}};
|
||||
const fileElements = Array.from(document.querySelectorAll("#files > tr"));
|
||||
|
||||
|
||||
applyNewOrder()
|
||||
|
||||
function getSearchParams() {
|
||||
return new URLSearchParams(window.location.hash.slice(1));
|
||||
}
|
||||
|
||||
function toggleSort(newSortProperty) {
|
||||
const params = getSearchParams()
|
||||
|
||||
const oldSortProperty = params.get("sort");
|
||||
if (oldSortProperty === newSortProperty) {
|
||||
const newDir = params.get("dir") === "asc" ? "desc" : "asc"
|
||||
window.location.hash = "sort=" + newSortProperty + "&dir=" + newDir
|
||||
} else {
|
||||
window.location.hash = "sort=" + newSortProperty
|
||||
|
||||
}
|
||||
setTimeout(() => applyNewOrder())
|
||||
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
function applyNewOrder() {
|
||||
const params = getSearchParams()
|
||||
const sortProperty = params.get("sort") ?? "lastModified"
|
||||
const ascending = params.get("dir") === "asc"
|
||||
sortFilesArray(sortProperty, ascending);
|
||||
updateRows();
|
||||
}
|
||||
|
||||
function sortFilesArray(propertyName, ascending) {
|
||||
|
||||
files.sort(compareByProp(propertyName))
|
||||
if (!ascending) {
|
||||
files.reverse()
|
||||
}
|
||||
}
|
||||
|
||||
function compareByProp(propertyName) {
|
||||
return (file1, file2) => {
|
||||
if (file1[propertyName] === file2[propertyName]) {
|
||||
return 0
|
||||
}
|
||||
if (file1[propertyName] > file2[propertyName]) {
|
||||
return 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
function updateRows() {
|
||||
let index = 0;
|
||||
for (const rowElement of fileElements) {
|
||||
update(rowElement, files[index++])
|
||||
}
|
||||
}
|
||||
|
||||
function update(rowElement, file) {
|
||||
const link = rowElement.querySelector('[data-col="key"] a')
|
||||
link.setAttribute("href", file.key)
|
||||
link.innerText = file.key
|
||||
|
||||
const size = rowElement.querySelector('[data-col="size"]')
|
||||
size.innerText = file.size
|
||||
|
||||
const lastModified = rowElement.querySelector('[data-col="lastModified"]')
|
||||
lastModified.innerText = file.lastModified
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,31 @@
|
||||
const crypto = require('crypto');
|
||||
|
||||
const { runTest } = require('./test-utils/runTest');
|
||||
const { createS3Client } = require('./s3client');
|
||||
const { generateFileList } = require('./generateFileList');
|
||||
const assert = require('node:assert');
|
||||
|
||||
// This is a test file. It is intended to be run manually with the proper environment variables set
|
||||
//
|
||||
// Run it from the project root using "node tasks/aws-s3-builds-page/generateFileList-test.js"
|
||||
|
||||
const s3Client = createS3Client();
|
||||
|
||||
runTest(async ({ log }) => {
|
||||
log('Generate file list');
|
||||
const filename = `test-file-list-${crypto.randomUUID()}`;
|
||||
await generateFileList(filename);
|
||||
|
||||
log(`Checking JSON at ${s3Client.fileUrl(`${filename}.json`)}`);
|
||||
const jsonList = JSON.parse(await s3Client.fetchFile(`${filename}.json`));
|
||||
assert(jsonList.find(s3obj => s3obj.key === 'handlebars-v4.7.7.js'));
|
||||
|
||||
log(`Checking HTML at ${s3Client.fileUrl(`${filename}.html`)}`);
|
||||
const htmlList = await s3Client.fetchFile(`${filename}.html`);
|
||||
assert(htmlList.includes('handlebars-v4.7.7.js'));
|
||||
assert(htmlList.includes('handlebarsjs.com'));
|
||||
assert(!htmlList.includes('index.html'));
|
||||
|
||||
log(`Deleting file ${filename}.json`);
|
||||
await s3Client.deleteFile(`${filename}.json`);
|
||||
});
|
||||
@@ -0,0 +1,39 @@
|
||||
/* eslint-disable no-console */
|
||||
const { createS3Client } = require('./s3client');
|
||||
const Handlebars = require('../..');
|
||||
const fs = require('node:fs/promises');
|
||||
const path = require('path');
|
||||
|
||||
async function generateFileList(nameWithoutExtension) {
|
||||
const s3Client = createS3Client();
|
||||
const fileList = await s3Client.listFiles();
|
||||
const relevantFiles = fileList.filter(s3obj => s3obj.key.endsWith('.js'));
|
||||
|
||||
await uploadJson(s3Client, relevantFiles, nameWithoutExtension);
|
||||
await uploadHtml(s3Client, relevantFiles, nameWithoutExtension);
|
||||
}
|
||||
|
||||
async function uploadJson(s3Client, fileList, nameWithoutExtension) {
|
||||
const fileListJson = JSON.stringify(fileList, null, 2);
|
||||
await s3Client.uploadData(fileListJson, nameWithoutExtension + '.json', {
|
||||
contentType: 'application/json'
|
||||
});
|
||||
}
|
||||
|
||||
async function uploadHtml(s3Client, fileList, nameWithoutExtension) {
|
||||
const templateStr = await fs.readFile(
|
||||
path.join(__dirname, 'fileList.hbs'),
|
||||
'utf-8'
|
||||
);
|
||||
const template = Handlebars.compile(templateStr);
|
||||
Handlebars.registerHelper('json', obj => JSON.stringify(obj));
|
||||
const fileListHtml = template({
|
||||
fileList,
|
||||
jsonListUrl: nameWithoutExtension + '.json'
|
||||
});
|
||||
await s3Client.uploadData(fileListHtml, nameWithoutExtension + '.html', {
|
||||
contentType: 'text/html'
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { generateFileList };
|
||||
@@ -0,0 +1,47 @@
|
||||
const crypto = require('crypto');
|
||||
const { publishWithSuffixes } = require('./publish');
|
||||
const { runTest } = require('./test-utils/runTest');
|
||||
const { createS3Client } = require('./s3client');
|
||||
const fs = require('node:fs/promises');
|
||||
|
||||
// This is a test file. It is intended to be run manually with the proper environment variables set
|
||||
//
|
||||
// Run it from the project root using "node tasks/aws-s3-builds-page/publish-test.js"
|
||||
|
||||
const s3Client = createS3Client();
|
||||
|
||||
runTest(async ({ log }) => {
|
||||
const suffix1 = `-test-file-` + crypto.randomUUID();
|
||||
const suffix2 = `-test-file-` + crypto.randomUUID();
|
||||
log(`Publish ${suffix1} and ${suffix2}`);
|
||||
await publishWithSuffixes([suffix1, suffix2]);
|
||||
await compareAndDeleteFiles(suffix1, log);
|
||||
await compareAndDeleteFiles(suffix2, log);
|
||||
});
|
||||
|
||||
async function compareAndDeleteFiles(suffix, log) {
|
||||
const pairs = [
|
||||
['dist/handlebars.js', `handlebars${suffix}.js`],
|
||||
['dist/handlebars.min.js', `handlebars.min${suffix}.js`],
|
||||
['dist/handlebars.runtime.js', `handlebars.runtime${suffix}.js`],
|
||||
['dist/handlebars.runtime.min.js', `handlebars.runtime.min${suffix}.js`]
|
||||
];
|
||||
for (const [localFile, remoteFile] of pairs) {
|
||||
await expectSameContents(localFile, remoteFile, log);
|
||||
log(`Deleting "${remoteFile}"`);
|
||||
await s3Client.deleteFile(remoteFile);
|
||||
}
|
||||
}
|
||||
|
||||
async function expectSameContents(localFile, remoteFile, log) {
|
||||
log(
|
||||
`Checking file contents "${localFile}" vs "${s3Client.fileUrl(remoteFile)}"`
|
||||
);
|
||||
const remoteContents = await s3Client.fetchFile(remoteFile);
|
||||
const localContents = await fs.readFile(localFile, 'utf-8');
|
||||
if (remoteContents !== localContents) {
|
||||
throw new Error(
|
||||
`Files do not match: ${localFile}" vs "${s3Client.fileUrl(remoteFile)}"`
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/* eslint-disable no-console */
|
||||
const { createS3Client } = require('./s3client');
|
||||
|
||||
const filenames = [
|
||||
'handlebars.js',
|
||||
'handlebars.min.js',
|
||||
'handlebars.runtime.js',
|
||||
'handlebars.runtime.min.js'
|
||||
];
|
||||
|
||||
async function publishWithSuffixes(suffixes) {
|
||||
const s3Client = createS3Client();
|
||||
const publishPromises = suffixes.map(suffix =>
|
||||
publishSuffix(s3Client, suffix)
|
||||
);
|
||||
return Promise.all(publishPromises);
|
||||
}
|
||||
|
||||
async function publishSuffix(s3client, suffix) {
|
||||
const publishPromises = filenames.map(async filename => {
|
||||
const nameInBucket = getNameInBucket(filename, suffix);
|
||||
const localFile = getLocalFile(filename);
|
||||
await s3client.uploadFile(localFile, nameInBucket);
|
||||
console.log(`Published ${localFile} to build server (${nameInBucket})`);
|
||||
});
|
||||
return Promise.all(publishPromises);
|
||||
}
|
||||
|
||||
function getNameInBucket(filename, suffix) {
|
||||
return filename.replace(/\.js$/, suffix + '.js');
|
||||
}
|
||||
|
||||
function getLocalFile(filename) {
|
||||
return 'dist/' + filename;
|
||||
}
|
||||
|
||||
module.exports = { publishWithSuffixes };
|
||||
@@ -0,0 +1,11 @@
|
||||
const { DeleteObjectCommand } = require('@aws-sdk/client-s3');
|
||||
|
||||
async function deleteFile(s3Client, bucket, remoteName) {
|
||||
const command = new DeleteObjectCommand({
|
||||
Bucket: bucket,
|
||||
Key: remoteName
|
||||
});
|
||||
await s3Client.send(command);
|
||||
}
|
||||
|
||||
module.exports = { deleteFile };
|
||||
@@ -0,0 +1,10 @@
|
||||
async function fetchFile(bucket, remoteName) {
|
||||
return (await fetch(fileUrl(bucket, remoteName))).text();
|
||||
}
|
||||
|
||||
function fileUrl(bucket, remoteName) {
|
||||
const bucketUrl = `https://s3.amazonaws.com/${bucket}`;
|
||||
return `${bucketUrl}/${remoteName}`;
|
||||
}
|
||||
|
||||
module.exports = { fetchFile, fileUrl };
|
||||
@@ -0,0 +1,42 @@
|
||||
const { listFiles } = require('./listFiles');
|
||||
const { uploadFile, uploadData } = require('./uploadFile');
|
||||
const { deleteFile } = require('./deleteFile');
|
||||
const { S3Client } = require('@aws-sdk/client-s3');
|
||||
const { requireEnvVar } = require('./requireEnvVar');
|
||||
const { fetchFile, fileUrl } = require('./fetchFile');
|
||||
|
||||
module.exports = { createS3Client };
|
||||
|
||||
function createS3Client() {
|
||||
// https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/loading-node-credentials-environment.html
|
||||
requireEnvVar('AWS_ACCESS_KEY_ID');
|
||||
requireEnvVar('AWS_SECRET_ACCESS_KEY');
|
||||
|
||||
const bucket = requireEnvVar('S3_BUCKET_NAME');
|
||||
const s3Client = new S3Client({
|
||||
region: 'us-east-1'
|
||||
});
|
||||
|
||||
return {
|
||||
async listFiles() {
|
||||
return listFiles(s3Client, bucket);
|
||||
},
|
||||
async uploadFile(localName, remoteName, { contentType } = {}) {
|
||||
await uploadFile(s3Client, bucket, localName, remoteName, {
|
||||
contentType
|
||||
});
|
||||
},
|
||||
async uploadData(data, remoteName, { contentType } = {}) {
|
||||
await uploadData(s3Client, bucket, data, remoteName, { contentType });
|
||||
},
|
||||
async deleteFile(remoteName) {
|
||||
await deleteFile(s3Client, bucket, remoteName);
|
||||
},
|
||||
async fetchFile(remoteName) {
|
||||
return fetchFile(bucket, remoteName);
|
||||
},
|
||||
fileUrl(remoteName) {
|
||||
return fileUrl(bucket, remoteName);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
const { ListObjectsV2Command } = require('@aws-sdk/client-s3');
|
||||
|
||||
async function listFiles(s3Client, bucket) {
|
||||
const command = new ListObjectsV2Command({
|
||||
Bucket: bucket
|
||||
});
|
||||
|
||||
let isTruncated = true;
|
||||
const files = [];
|
||||
|
||||
while (isTruncated) {
|
||||
const {
|
||||
Contents,
|
||||
IsTruncated,
|
||||
NextContinuationToken
|
||||
} = await s3Client.send(command);
|
||||
files.push(...Contents.map(dataFromS3Object));
|
||||
isTruncated = IsTruncated;
|
||||
command.input.ContinuationToken = NextContinuationToken;
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
function dataFromS3Object(s3obj) {
|
||||
return {
|
||||
key: s3obj.Key,
|
||||
size: s3obj.Size,
|
||||
lastModified: s3obj.LastModified.toISOString()
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = { listFiles };
|
||||
@@ -0,0 +1,8 @@
|
||||
function requireEnvVar(name) {
|
||||
if (!process.env[name]) {
|
||||
throw new Error(`Environment variable "${name}" is required.`);
|
||||
}
|
||||
return process.env[name];
|
||||
}
|
||||
|
||||
module.exports = { requireEnvVar };
|
||||
@@ -0,0 +1,89 @@
|
||||
/* eslint-disable no-console */
|
||||
const { createS3Client } = require('./index');
|
||||
const crypto = require('crypto');
|
||||
const { runTest } = require('../test-utils/runTest');
|
||||
const assert = require('node:assert');
|
||||
|
||||
// This is a test file. It is intended to be run manually
|
||||
// with the proper environment variables set
|
||||
// It tests whether the upload/list/delete methods in this directory
|
||||
// work properly.
|
||||
//
|
||||
// Run it from the project root using "node tasks/aws-s3-builds-page/s3client/s3-test.js"
|
||||
|
||||
const client = createS3Client();
|
||||
|
||||
const ISO_DATE = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/;
|
||||
|
||||
runTest(async ({ log }) => {
|
||||
const uuid = crypto.randomUUID();
|
||||
const filename = `test-file-${uuid}`;
|
||||
log(`Starting test with target file "${filename}"`);
|
||||
|
||||
log(`Uploading "${filename}"`);
|
||||
await client.uploadFile('package.json', filename);
|
||||
|
||||
log(`Check if uploaded "${filename}"`);
|
||||
const listing = await client.listFiles();
|
||||
if (!listing.find(s3obj => s3obj.key === filename)) {
|
||||
throw new Error(`File "${filename}" has not been uploaded`);
|
||||
}
|
||||
|
||||
log(`Check contents of "${filename}"`);
|
||||
const uploadedContents = await client.fetchFile(filename);
|
||||
expectStringContains('"name": "handlebars"', uploadedContents);
|
||||
await expectContentType(filename, 'application/octet-stream');
|
||||
|
||||
log(`Uploading with content type "${filename}"`);
|
||||
await client.uploadFile('package.json', filename, {
|
||||
contentType: 'text/html'
|
||||
});
|
||||
log('Checking content-type');
|
||||
await expectContentType(filename, 'text/html');
|
||||
|
||||
log('Upload data as text/plain');
|
||||
await client.uploadData('Hello world', filename, {
|
||||
contentType: 'text/plain'
|
||||
});
|
||||
log('Checking content-type');
|
||||
await expectContentType(filename, 'text/plain');
|
||||
log(`Check contents of "${filename}"`);
|
||||
expectStringContains('Hello world', await client.fetchFile(filename));
|
||||
const helloWorldObj = (await client.listFiles()).find(
|
||||
s3obj => s3obj.key === filename
|
||||
);
|
||||
assert.equal(helloWorldObj.size, 11, 'Checking file size of hello world');
|
||||
assert.match(
|
||||
helloWorldObj.lastModified,
|
||||
ISO_DATE,
|
||||
'Last modified must be an iso-date'
|
||||
);
|
||||
|
||||
log(`Delete "${filename}"`);
|
||||
await client.deleteFile(filename);
|
||||
|
||||
log(`Check if deleted "${filename}"`);
|
||||
const foundFile = (await client.listFiles()).find(
|
||||
s3obj => s3obj.key === filename
|
||||
);
|
||||
if (foundFile != null) {
|
||||
throw new Error(`File "${filename}" has not been deleted`);
|
||||
}
|
||||
});
|
||||
|
||||
function expectStringContains(needle, haystack) {
|
||||
if (!haystack.includes(needle)) {
|
||||
throw new Error(`Expecting to find "${needle}" in string "${haystack}"`);
|
||||
}
|
||||
}
|
||||
|
||||
async function expectContentType(remoteName, expectedContentType) {
|
||||
const contentType = (await fetch(client.fileUrl(remoteName))).headers.get(
|
||||
'Content-Type'
|
||||
);
|
||||
if (contentType !== expectedContentType) {
|
||||
throw new Error(
|
||||
`Expecting to find content-type "${expectedContentType}" but found "${contentType}"`
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
const { PutObjectCommand } = require('@aws-sdk/client-s3');
|
||||
const fs = require('node:fs/promises');
|
||||
|
||||
async function uploadFile(
|
||||
s3Client,
|
||||
bucket,
|
||||
localName,
|
||||
remoteName,
|
||||
{ contentType } = {}
|
||||
) {
|
||||
const fileContents = await fs.readFile(localName);
|
||||
await uploadData(s3Client, bucket, fileContents, remoteName, { contentType });
|
||||
}
|
||||
|
||||
async function uploadData(
|
||||
s3Client,
|
||||
bucket,
|
||||
data,
|
||||
remoteName,
|
||||
{ contentType } = {}
|
||||
) {
|
||||
const command = new PutObjectCommand({
|
||||
Bucket: bucket,
|
||||
Key: remoteName,
|
||||
Body: data,
|
||||
ContentType: contentType
|
||||
});
|
||||
await s3Client.send(command);
|
||||
}
|
||||
|
||||
module.exports = { uploadFile, uploadData };
|
||||
@@ -0,0 +1,37 @@
|
||||
/* eslint-disable no-console */
|
||||
const { createS3Client } = require('../s3client/index');
|
||||
|
||||
const s3Client = createS3Client();
|
||||
|
||||
function runTest(asyncFn) {
|
||||
asyncFn({ log: console.log.bind(console) })
|
||||
.finally(detectSurplusFiles)
|
||||
.then(() => {
|
||||
console.log('DONE');
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
async function detectSurplusFiles() {
|
||||
const listing = await s3Client.listFiles();
|
||||
let surplusFileDetected = false;
|
||||
const testFilesInBucket = listing.filter(name =>
|
||||
name.key.includes('test-file')
|
||||
);
|
||||
for (const { key: filename } of testFilesInBucket) {
|
||||
if (process.argv[2] === '--delete-surplus') {
|
||||
await s3Client.deleteFile(filename);
|
||||
} else {
|
||||
console.log(`Detected surplus file "${filename}"`);
|
||||
surplusFileDetected = true;
|
||||
}
|
||||
}
|
||||
if (surplusFileDetected) {
|
||||
console.log(`run with --delete-surplus to delete surplus files`);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { runTest };
|
||||
+4
-71
@@ -1,7 +1,8 @@
|
||||
const AWS = require('aws-sdk');
|
||||
const git = require('./util/git');
|
||||
const { createRegisterAsyncTaskFn } = require('./util/async-grunt-task');
|
||||
const semver = require('semver');
|
||||
const { publishWithSuffixes } = require('./aws-s3-builds-page/publish');
|
||||
const { generateFileList } = require('./aws-s3-builds-page/generateFileList');
|
||||
|
||||
module.exports = function(grunt) {
|
||||
const registerAsyncTask = createRegisterAsyncTaskFn(grunt);
|
||||
@@ -27,76 +28,8 @@ module.exports = function(grunt) {
|
||||
}
|
||||
|
||||
if (suffixes.length > 0) {
|
||||
initSDK();
|
||||
grunt.log.writeln(
|
||||
'publishing file-suffixes: ' + JSON.stringify(suffixes)
|
||||
);
|
||||
await publish(suffixes);
|
||||
await publishWithSuffixes(suffixes);
|
||||
await generateFileList('index');
|
||||
}
|
||||
});
|
||||
|
||||
function initSDK() {
|
||||
const bucket = process.env.S3_BUCKET_NAME,
|
||||
key = process.env.S3_ACCESS_KEY_ID,
|
||||
secret = process.env.S3_SECRET_ACCESS_KEY;
|
||||
|
||||
if (!bucket || !key || !secret) {
|
||||
throw new Error('Missing S3 config values');
|
||||
}
|
||||
|
||||
AWS.config.update({ accessKeyId: key, secretAccessKey: secret });
|
||||
}
|
||||
|
||||
async function publish(suffixes) {
|
||||
const publishPromises = suffixes.map(suffix => publishSuffix(suffix));
|
||||
return Promise.all(publishPromises);
|
||||
}
|
||||
|
||||
async function publishSuffix(suffix) {
|
||||
const filenames = [
|
||||
'handlebars.js',
|
||||
'handlebars.min.js',
|
||||
'handlebars.runtime.js',
|
||||
'handlebars.runtime.min.js'
|
||||
];
|
||||
const publishPromises = filenames.map(async filename => {
|
||||
const nameInBucket = getNameInBucket(filename, suffix);
|
||||
const localFile = getLocalFile(filename);
|
||||
await uploadToBucket(localFile, nameInBucket);
|
||||
grunt.log.writeln(
|
||||
`Published ${localFile} to build server (${nameInBucket})`
|
||||
);
|
||||
});
|
||||
return Promise.all(publishPromises);
|
||||
}
|
||||
|
||||
async function uploadToBucket(localFile, nameInBucket) {
|
||||
const bucket = process.env.S3_BUCKET_NAME;
|
||||
const uploadParams = {
|
||||
Bucket: bucket,
|
||||
Key: nameInBucket,
|
||||
Body: grunt.file.read(localFile)
|
||||
};
|
||||
return s3PutObject(uploadParams);
|
||||
}
|
||||
};
|
||||
|
||||
function s3PutObject(uploadParams) {
|
||||
const s3 = new AWS.S3();
|
||||
return new Promise((resolve, reject) => {
|
||||
s3.putObject(uploadParams, err => {
|
||||
if (err != null) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getNameInBucket(filename, suffix) {
|
||||
return filename.replace(/\.js$/, suffix + '.js');
|
||||
}
|
||||
|
||||
function getLocalFile(filename) {
|
||||
return 'dist/' + filename;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ cd "$( dirname "$( readlink -f "$0" )" )" || exit 1
|
||||
unset npm_config_prefix
|
||||
|
||||
echo "Handlebars should be able to run in various versions of NodeJS"
|
||||
for node_version_to_test in 0.10 0.12 4 5 6 7 8 9 10 11 12 13 14 15 16 17; do
|
||||
for node_version_to_test in 0.10 0.12 4 5 6 7 8 9 10 11 12 13 14 15 16 18; do
|
||||
|
||||
rm target node_modules package-lock.json -rf
|
||||
mkdir target
|
||||
|
||||
@@ -4,7 +4,7 @@ set -e
|
||||
|
||||
# Cleanup: package-lock and "npm ci" is not working with local dependencies
|
||||
rm dist package-lock.json -rf
|
||||
npm install
|
||||
npm install --legacy-peer-deps
|
||||
npm run build
|
||||
|
||||
for i in dist/*-test.js ; do
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "webpack-test",
|
||||
"description": "Various tests with Handlebars and Webpack",
|
||||
"description": "Various tests with Handlebars and multiple webpack versions",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
@@ -8,14 +8,8 @@
|
||||
"test": "node dist/main.js"
|
||||
},
|
||||
"private": true,
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"dependencies": {
|
||||
"handlebars": "file:../../..",
|
||||
"handlebars-loader": "^1.7.1",
|
||||
"webpack": "^4.39.3",
|
||||
"webpack-cli": "^3.3.7"
|
||||
"handlebars-loader": "^1.7.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import Handlebars from 'handlebars/lib/handlebars';
|
||||
import { assertEquals } from './lib/assert';
|
||||
|
||||
const template = Handlebars.compile('Author: {{author}}');
|
||||
assertEquals(template({ author: 'Yehuda' }), 'Author: Yehuda');
|
||||
@@ -0,0 +1,43 @@
|
||||
import * as Handlebars from 'handlebars/runtime';
|
||||
import { assertEquals } from './lib/assert';
|
||||
|
||||
const template = Handlebars.template({
|
||||
compiler: [8, '>= 4.3.0'],
|
||||
main: function(container, depth0, helpers, partials, data) {
|
||||
var helper,
|
||||
lookupProperty =
|
||||
container.lookupProperty ||
|
||||
function(parent, propertyName) {
|
||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||
return parent[propertyName];
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
return (
|
||||
'Author: ' +
|
||||
container.escapeExpression(
|
||||
((helper =
|
||||
(helper =
|
||||
lookupProperty(helpers, 'author') ||
|
||||
(depth0 != null ? lookupProperty(depth0, 'author') : depth0)) !=
|
||||
null
|
||||
? helper
|
||||
: container.hooks.helperMissing),
|
||||
typeof helper === 'function'
|
||||
? helper.call(depth0 != null ? depth0 : container.nullContext || {}, {
|
||||
name: 'author',
|
||||
hash: {},
|
||||
data: data,
|
||||
loc: {
|
||||
start: { line: 1, column: 8 },
|
||||
end: { line: 1, column: 18 }
|
||||
}
|
||||
})
|
||||
: helper)
|
||||
)
|
||||
);
|
||||
},
|
||||
useData: true
|
||||
});
|
||||
assertEquals(template({ author: 'Yehuda' }), 'Author: Yehuda');
|
||||
@@ -2,11 +2,7 @@
|
||||
|
||||
set -e
|
||||
|
||||
# Cleanup: package-lock and "npm ci" is not working with local dependencies
|
||||
rm dist package-lock.json -rf
|
||||
npm install
|
||||
npm run build
|
||||
|
||||
run_tests () {
|
||||
for i in dist/*-test.js ; do
|
||||
echo "----------------------"
|
||||
echo "-- Running $i"
|
||||
@@ -14,3 +10,18 @@ for i in dist/*-test.js ; do
|
||||
node "$i"
|
||||
echo "Success"
|
||||
done
|
||||
}
|
||||
|
||||
# Cleanup: package-lock and "npm ci" is not working with local dependencies
|
||||
rm dist package-lock.json -rf
|
||||
npm install --legacy-peer-deps
|
||||
|
||||
# Test with webpack 4
|
||||
npm install --legacy-peer-deps --no-save webpack@^4 webpack-cli@^3
|
||||
npm run build
|
||||
run_tests
|
||||
|
||||
# Test with webpack 5
|
||||
npm install --legacy-peer-deps --no-save webpack@^5 webpack-cli@^4
|
||||
npm run build
|
||||
run_tests
|
||||
Reference in New Issue
Block a user