Build, test and deploy
A project is built by grunt. Its Gruntfile.js defines five tasks, and amber init ran two of them for you. This page shows each task on the Hello Amber project from the install page. For each, it says what the task prints and what it writes. Every task starts by regenerating config.js, the loader configuration, from the .amd.json files of the project and its dependencies. The project page says what that file holds.
Node.js 24 prints a DeprecationWarning about a child process when a task runs a command. It comes from grunt-exec and changes nothing.
grunt: compile the packages
grunt with no task name runs default, which compiles every .st file in src/ to its .js:
Running "amdconfig:app" (amdconfig) task
Running "amberc:all" (amberc) task
Compiling with @ambers/lang 0.32.0 from .../node_modules/@ambers/lang (@ambers/sdk 0.13.1 expects >0.22.6)
Reading: src/HelloAmber.st
Reading: src/HelloAmber-Backend.st
Reading: src/HelloAmber-Tests.st
Done.
The compiler is amberc, from @ambers/sdk. It loads Amber from node_modules/@ambers/lang and the libraries named in the Gruntfile. Then it reads the .st files in the order listed and writes a .js beside each. The amberc section of the Gruntfile holds three things:
all: {
src: [
'src/HelloAmber.st', 'src/HelloAmber-Backend.st', // list all sources in dependency order
'src/HelloAmber-Tests.st' // list all tests in dependency order
],
amd_namespace: 'amber-helloamber',
libraries: ['amber/core/SUnit', 'amber/web/Web', 'silk/Silk']
}
src lists the files to compile. A package that uses a class of another package in the list comes after it. amd_namespace is the project’s namespace, as in local.amd.json. libraries names the modules the compiler loads before it compiles, so that it knows the classes the sources use. The sample uses TestCase from SUnit, the brushes of Web and Silk. When your package imports a new library, add its module here too.
When you commit a package in Helios, the server writes the .js with the .st. So this task is not needed after every change. It is needed when you edit a .st file by hand, or pull a change. It is needed when a new Amber compiles differently.
grunt devel: the page for development
grunt devel builds the.js for development:
Running "amdconfig:app" (amdconfig) task
Running "requirejs:devel" (requirejs) task
Done.
The requirejs optimizer writes a the.js of about ninety kilobytes. It holds the loader, the configuration and the entry point, and nothing of the application. The page loads each package from its own file, so what Helios commits is what the next reload runs. Run grunt devel after npm install, after a change to a .amd.json file, and after grunt deploy, to get the development page back.
grunt test: the tests under Node.js
grunt test runs the test packages under Node.js, with no browser. npm test is the same. The sample’s HelloAmberTest has no test method; this one was added to it:
testGreeting
self assert: 'Hello', ' ', 'Amber' equals: 'Hello Amber'
With it, the task prints:
Running "amdconfig:app" (amdconfig) task
Running "requirejs:test_runner" (requirejs) task
Running "exec:test_runner" (exec) task
1 tests run, 0 failures, 0 errors.
Running "clean:test_runner" (clean) task
>> 1 path cleaned.
Done.
The optimizer bundles the packages of testing.js with Amber’s test runner into test_runner.js, a Node.js script. The task runs it and deletes it. The runner loads the packages, runs every TestCase subclass it finds, and prints the counts.
When a test fails, the runner runs it once more on its own. It prints the stack of the failure and its message, and exits with a code of 1:
HelloAmberTest >> #testGreeting
HelloAmberTest (TestCase) >> #assert:equals:
...
Expected: 'Hello World' but was: 'Hello Amber'
>> Exited with code: 1.
Warning: Task "exec:test_runner" failed. Use --force to continue.
Aborted due to warnings.
Grunt stops there, so test_runner.js stays behind. It is in .gitignore, and the next run replaces it.
A test that needs the browser cannot run here. The Helios page shows the test runner in the IDE, which runs the same tests in the page.
grunt deploy: the page for release
grunt deploy builds the.js for release:
Running "amdconfig:app" (amdconfig) task
Running "requirejs:deploy" (requirejs) task
Running "exec:minify_deploy" (exec) task
> helloamber@0.1.0 minify:deploy
> terser the.js --compress --mangle --output the.js
Done.
The optimizer follows the packages of deploy.js through every dependency, Amber’s kernel included. It writes them all into one the.js. Two pragmas strip what only the IDE needs: the source and the protocols of each method, and the debugging contexts. Then terser minifies the file. For the sample, with Web, Silk and jQuery, the result is about 550 kilobytes.
The deployed application is index.html, the.js, and any file of your own the page loads. Copy them to a static server. node_modules stays at home. The starter dialog does not appear, because its module is not in the bundle. The page finds nothing to show and starts the application. To develop again, run grunt devel.
grunt deploy:lambda: the backend
grunt deploy:lambda does the same for the packages of lambda.js, with Amber’s Node.js platform in place of the browser’s. It writes lambda/the.js, minified. lambda/index.js loads it:
const app = require('./the.js')('HelloAmberBackend');
exports.status = app('status');
exports.echo = app('echoEvent:andContext:');
The bundle exports a function. Give it a class name, and it answers a function that takes a selector and answers a handler. The handler creates the instance when Amber has loaded, and sends the selector with the event and the context. The lambda directory is what you upload to AWS Lambda, with index.status and index.echo as the handlers.
The npm scripts
package.json has six scripts. npm run init runs npm install and grunt default devel. It is what amber init ran at the end, and what a fresh clone of your project needs. npm run clean deletes node_modules, and npm run reset runs both. npm test runs grunt test. The two minify scripts are the last step of the two deploy tasks.