Browse Source

Merge pull request #979 from amber-smalltalk/include-amber-cli

Include amber cli
Nicolas Petton 10 years ago
parent
commit
0a78fd605e

+ 44 - 0
external/amber-cli/CHANGELOG

@@ -0,0 +1,44 @@
+XXth XXX 2014 - Release 0.1.0
+===================================
+
+Highlights:
+
+* Package is polished.
+* `amber-cli` is now official package to install globally.
+
+Commits: https://github.com/amber-smalltalk/amber-cli/compare/0.0.6...0.1.0
+Issues:  https://github.com/amber-smalltalk/amber-cli/issues?milestone=2&state=closed
+
+
+15th April 2014 - Release 0.0.6
+===================================
+
+Highlights:
+
+* `amberc -d dir` allows specify alternate Amber dir.
+
+Commits: https://github.com/amber-smalltalk/amber-cli/compare/0.0.5...0.0.6
+Issues:  https://github.com/amber-smalltalk/amber-cli/issues?milestone=4&state=closed
+
+
+15th April 2014 - Release 0.0.5
+===================================
+
+Highlights:
+
+* Bugfix: `amberc -l` now working.
+
+Commits: https://github.com/amber-smalltalk/amber-cli/compare/0.0.4...0.0.5
+Issues:  https://github.com/amber-smalltalk/amber-cli/issues?milestone=3&state=closed
+
+
+14th April 2014 - Release 0.0.4
+===================================
+
+Highlights:
+
+* Amber tooling is now in its own package.
+
+Commits: https://github.com/amber-smalltalk/amber-cli/compare/cc2a163...0.0.4
+Issues:  https://github.com/amber-smalltalk/amber-cli/issues?milestone=1&state=closed
+

+ 63 - 0
external/amber-cli/CONTRIBUTING.md

@@ -0,0 +1,63 @@
+Start Contributing by talking about Amber
+=========================================
+
+* Join our [Mailinglist/Google Group](http://groups.google.com/group/amber-lang)
+* Talk to us on [the #amber-lang IRC channel](irc://irc.freenode.net/amber-lang)
+* Follow [@AmberSmalltalk](https://twitter.com/AmberSmalltalk) on Twitter
+* Circle Amber Smalltalk on [Google+](https://plus.google.com/u/0/107038882958653788078) 
+
+
+Filing Issues
+=============
+
+If you think Amber CLI is not working as expected, You can start by asking on IRC or the Mailinglist.
+Please make sure that you have first checked the following guides:
+
+* [Getting Started](https://github.com/amber-smalltalk/amber/wiki/Getting-started)
+* [Writing My First App](https://github.com/amber-smalltalk/amber/wiki/Writing-my-first-app)
+* [How To Load Amber](https://github.com/amber-smalltalk/amber/wiki/How-to-load-amber)
+* [Amber FAQ](https://github.com/amber-smalltalk/amber/wiki/FAQ)
+
+If the issue can not be resolved you should file an issue on the respective tracker.
+
+Before reporting an issue, try to reduce the issue to the bare minimum required to reproduce it.
+This allows us to track down and fix the issue in an easier and faster way.
+
+Additionally, you should give us enough information to reproduce the issue.
+Therefore, include versions of your OS, Amber, Node.js, Grunt, and possibly used libraries as well as sample code.
+If you don't list the exact steps required to reproduce the issue we won't be able to fix it.
+
+
+Creating a Pull Request
+-----------------------
+
+The Amber development model currently revolves around Pull Requests which are created through GitHub
+
+1. Update to latest Amber CLI master (```git pull```)
+2. Develop your feature or bugfix in a local branch (not in ```master```)
+3. Create unittest for your feature or bugfix (your feature/fix will be integrated a lot faster if unittests are present)
+4. Enhance/fix Amber
+5. Run the unittests
+6. Commit your changes to disk if all tests are green
+7. Try to split your fix into small Git commits if multiple changes are involved (this makes it easier for us to review the changes)
+8. If you created / deleted / moved something significant, update CHANGELOG appropriately and commit.
+8. Push the changes to your fork on GitHub ```git push <your repo> <your branchname>```
+9. Submit Pull Request (usually for the Amber CLI master branch)
+
+
+Compiling Amber CLI with Grunt
+--------------------------
+
+Amber uses [Grunt.js](http://gruntjs.com/) as build system since version `0.10.0`.
+
+To install Grunt.js v0.4.x on the commandline execute the following commands:
+
+    npm install -g grunt-cli
+
+Make sure that you have installed all required dependencies via `npm` and `bower`.
+Then you can finally compile Amber CLI using the following command:
+
+    cd ${Amber_DIR}
+    grunt amberc:amber_cli
+
+For Windows support check the [Grunt.js on Windows](http://gruntjs.com/frequently-asked-questions#does-grunt-work-on-windows) page.

+ 45 - 0
external/amber-cli/Gruntfile.js

@@ -0,0 +1,45 @@
+module.exports = function(grunt) {
+  var path = require('path');
+
+  grunt.loadNpmTasks('amber-dev');
+
+  grunt.loadNpmTasks('grunt-contrib-jshint');
+
+  grunt.registerTask('default', ['amberc:cli']);
+
+  grunt.initConfig({
+    pkg: grunt.file.readJSON('package.json'),
+
+    meta: {
+      banner: '/*!\n <%= pkg.title || pkg.name %> - v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %> \n License: <%= pkg.license.type %> \n*/\n'
+    },
+
+    amberc: {
+      options: {
+        amber_dir: path.normalize(path.join(__dirname, "node_modules", "amber"))
+      },
+      amber_tests: {
+        output_dir : 'src',
+        src: ['src/Kernel-Tests.st', 'src/Compiler-Tests.st', 'src/SUnit-Tests.st'],
+        libraries: ['SUnit']
+      },
+      cli: {
+        output_dir: 'cli/src',
+        src: ['cli/src/AmberCli.st'],
+        libraries: [
+            'Compiler-Exceptions', 'Compiler-Core', 'Compiler-AST',
+            'Compiler-IR', 'Compiler-Inlining', 'Compiler-Semantic', 'Compiler-Interpreter', 'parser',
+            'SUnit', 'Kernel-ImportExport',
+            'Kernel-Tests', 'Compiler-Tests', 'SUnit-Tests'
+        ],
+        main_class: 'AmberCli',
+        output_name: '../support/amber-cli',
+        amd_namespace: 'amber_cli'
+      }
+    },
+
+    jshint: {
+      cli: ['cli/src/*.js', 'cli/support/*.js']
+    }
+  });
+};

+ 22 - 0
external/amber-cli/LICENSE

@@ -0,0 +1,22 @@
+Copyright (C) 2011-2014 Nicolas Petton <petton.nicolas@gmail.com>
+Copyright (C) 2011-2014 Amber contributors https://github.com/NicolasPetton/amber/contributors
+
+Parts of Amber take ideas from Clamato (http://clamato.net), written by Avi Bryant.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

+ 47 - 0
external/amber-cli/README.md

@@ -0,0 +1,47 @@
+Amber [![Travis CI Status](https://secure.travis-ci.org/amber-smalltalk/amber.png)](https://travis-ci.org/#!/amber-smalltalk/amber-cli)
+=====
+
+By Nicolas Petton <petton.nicolas@gmail.com> and [Amber contributors](https://github.com/amber-smalltalk/amber/contributors)
+
+Amber is an implementation of the Smalltalk language that runs on top of the JavaScript runtime. It is designed to make client-side development faster and easier.
+
+Overview
+--------
+
+Amber is written in itself, including the parser and compiler. Amber compiles into efficient JavaScript, mapping one-to-one with the equivalent JavaScript. There is no interpretation at runtime.
+
+Some highlights:
+
+-    Amber features an IDE with a Class browser, Workspace, Transcript, a ReferencesBrowser supporting senders/implementors and class references, basic Inspector and even a beginning of a Debugger and a unit TestRunner.
+-    [Pharo Smalltalk](http://www.pharo-project.org) is considered as the reference implementation.
+-    Amber includes a canvas to generate HTML, like [Seaside](http://www.seaside.st)
+-    Amber can use Javascript libraries and the current IDE is built on [jQuery](http://www.jquery.com)
+-    You can inline Javascript code and there are many ways to interact between Amber and Javascript
+
+Getting Amber
+-------------
+
+Amber is shipped as a [npm](http://npmjs.org) package for its CLI tools and as a [bower](https://github.com/bower/bower) package for the client-side.
+
+    # Install the CLI tool `amber`
+    npm install -g amber-cli
+    
+    # Initialize your project
+    cd /path/to/myproject
+    amber init
+
+    # Serve amber on localhost:4000
+    amber serve
+
+The [Getting started](http://docs.amber-lang.net/getting-started.html) page shows more details on ways to obtain Amber and start a project.
+
+License
+-------
+
+Amber is released under the MIT license. All contributions made for inclusion are considered to be under MIT.
+
+
+More infos
+----------
+
+More on the [project page](http://amber-lang.net)

+ 15 - 0
external/amber-cli/RELEASING.md

@@ -0,0 +1,15 @@
+Release Guide for Amber
+=======================
+
+The following steps are required to make a release of Amber:
+
+1. (not yet) check that all tests are green
+1. check the `CHANGELOG` file and update the release notes, check the milestone index in issues link
+1. bump version and git repo tag in `package.json`
+1. Create a tag for bumped version:`git tag -a 1.2.3 -m "Version 1.2.3`
+1. `git commit -a -m "Release 1.2.3" && git push && git push --tags`
+1. log in to npm with write access for the Amber package
+1. update the homepage to point to the latest tag on GitHub
+1. send announcement to mailinglists (Amber, Pharo, what else?)
+1. send announcement on Twitter
+1. send announcement on G+

+ 18 - 0
external/amber-cli/cli/bower.json

@@ -0,0 +1,18 @@
+{
+  "name": "amber-cli",
+  "version": "0.0.1",
+  "homepage": "https://github.com/amber-smalltalk/amber-cli",
+  "description": "CLI for Amber Smalltalk",
+  "license": "MIT",
+  "private": true,
+  "ignore": [
+    "**/.*",
+    "node_modules",
+    "bower_components",
+    "test",
+    "tests"
+  ],
+  "dependencies": {
+    "amber": "~0.12.4"
+  }
+}

+ 32 - 0
external/amber-cli/cli/index.html

@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+
+  <head>
+    <title>Amber Smalltalk</title>
+    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+    <meta name="author" content="Nicolas Petton" />
+    <script type='text/javascript' src='bower_components/amber/support/requirejs/require.min.js'></script>
+    <script src='bower_components/amber/support/amber.js'></script>
+
+  </head>
+
+  <body>
+    <script type='text/javascript'>
+    require.config({
+        paths: {
+            'amber_cli': 'src'
+        }
+    });
+    require(
+        ["amber/devel",
+            "amber_cli/AmberCli" ],
+        function (smalltalk) {
+            smalltalk.initialize({'transport.defaultAmdNamespace': 'amber_cli'});
+//            smalltalk.globals.Browser._openOn_(smalltalk.AmberCli);
+            smalltalk.popupHelios();
+        }
+    );
+    </script>
+  </body>
+</html> 
+

File diff suppressed because it is too large
+ 1233 - 0
external/amber-cli/cli/src/AmberCli.js


+ 1232 - 0
external/amber-cli/cli/src/AmberCli.st

@@ -0,0 +1,1232 @@
+Smalltalk createPackage: 'AmberCli'!
+Object subclass: #AmberCli
+	instanceVariableNames: ''
+	package: 'AmberCli'!
+!AmberCli commentStamp!
+I am the Amber CLI (CommandLine Interface) tool which runs on Node.js.
+
+My responsibility is to start different Amber programs like the FileServer or the Repl.
+Which program to start is determined by the first commandline parameters passed to the AmberCli executable.
+Use `help` to get a list of all available options.
+Any further commandline parameters are passed to the specific program.
+
+## Commands
+
+New commands can be added by creating a class side method in the `commands` protocol which takes one parameter.
+This parameter is an array of all commandline options + values passed on to the program.
+Any `camelCaseCommand` is transformed into a commandline parameter of the form `camel-case-command` and vice versa.!
+
+!AmberCli class methodsFor: 'commandline'!
+
+commandLineSwitches
+	"Collect all methodnames from the 'commands' protocol of the class
+	 and select the ones with only one parameter.
+	 Then remove the ':' at the end of the name.
+	 Additionally all uppercase letters are made lowercase and preceded by a '-'.
+	 Example: fallbackPage: becomes --fallback-page.
+	 Return the Array containing the commandline switches."
+	| switches |
+	switches := ((self class methodsInProtocol: 'commands') collect: [ :each | each selector]).
+	switches := switches select: [ :each | each match: '^[^:]*:$'].
+	switches :=switches collect: [ :each |
+		(each allButLast replace: '([A-Z])' with: '-$1') asLowercase].
+	^ switches
+!
+
+handleArguments: args
+	| selector |
+
+	selector := self selectorForCommandLineSwitch: (args first).
+	args remove: args first.
+	self perform: selector  withArguments: (Array with: args)
+!
+
+selectorForCommandLineSwitch: aSwitch
+	"Add ':' at the end and replace all occurences of a lowercase letter preceded by a '-' with the Uppercase letter.
+	 Example: fallback-page becomes fallbackPage:.
+	 If no correct selector is found return 'help:'"
+	 | command selector |
+
+	 (self commandLineSwitches includes: aSwitch)
+	 ifTrue: [ selector := (aSwitch replace: '-[a-z]' with: [ :each | each second asUppercase ]), ':']
+	 ifFalse: [ selector := 'help:' ].
+	^ selector
+! !
+
+!AmberCli class methodsFor: 'commands'!
+
+help: args
+	Transcript show: 'Available commands'.
+	self commandLineSwitches do: [ :each | console log: each ]
+!
+
+init: args
+	Initer new start
+!
+
+repl: args
+	^ Repl new createInterface
+!
+
+serve: args
+	^ (FileServer createServerWithArguments: args) start
+!
+
+tests: arguments
+	^ NodeTestRunner runTestSuite
+!
+
+version: arguments
+! !
+
+!AmberCli class methodsFor: 'startup'!
+
+main
+	"Main entry point for Amber applications.
+	Parses commandline arguments and starts the according subprogram."
+	| args nodeMinorVersion |
+	
+	Transcript show: 'Welcome to Amber version ', Smalltalk version, ' (NodeJS ', process versions node, ').'.
+
+	nodeMinorVersion := ((process version) tokenize: '.') second asNumber.
+	nodeMinorVersion < 8 ifTrue: [
+		Transcript show: 'You are currently using Node.js ', (process version).
+		Transcript show: 'Required is at least Node.js v0.8.x or greater.'.
+		^ -1.
+	].
+
+	args := process argv.
+	"Remove the first args which contain the path to the node executable and the script file."
+	args removeFrom: 1 to: 2.
+	
+	(args isEmpty)
+		ifTrue: [self help: nil]
+		ifFalse: [^self handleArguments: args]
+! !
+
+Object subclass: #FileServer
+	instanceVariableNames: 'path http fs url host port basePath util username password fallbackPage'
+	package: 'AmberCli'!
+!FileServer commentStamp!
+I am the Amber Smalltalk FileServer.
+My runtime requirement is a functional Node.js executable.
+
+To start a FileServer instance on port `4000` use the following code:
+
+    FileServer new start
+
+A parameterized instance can be created with the following code:
+
+    FileServer createServerWithArguments: options
+
+Here, `options` is an array of commandline style strings each followed by a value e.g. `#('--port', '6000', '--host', '0.0.0.0')`.
+A list of all available parameters can be printed to the commandline by passing `--help` as parameter.
+See the `Options` section for further details on how options are mapped to instance methods.
+
+After startup FileServer checks if the directory layout required by Amber is present and logs a warning on absence.
+
+
+## Options
+
+Each option is of the form `--some-option-string` which is transformed into a selector of the format `someOptionString:`.
+The trailing `--` gets removed, each `-[a-z]` gets transformed into the according uppercase letter, and a `:` is appended to create a selector which takes a single argument.
+Afterwards, the selector gets executed on the `FileServer` instance with the value following in the options array as parameter.
+
+## Adding new commandline parameters
+
+Adding new commandline parameters to `FileServer` is as easy as adding a new single parameter method to the `accessing` protocol.!
+
+!FileServer methodsFor: 'accessing'!
+
+basePath
+	^ basePath ifNil: [self class defaultBasePath]
+!
+
+basePath: aString
+	basePath := aString.
+	self validateBasePath.
+!
+
+fallbackPage
+	^ fallbackPage
+!
+
+fallbackPage: aString
+	fallbackPage := aString
+!
+
+host
+	^ host
+!
+
+host: hostname
+	host := hostname
+!
+
+password: aPassword
+	password := aPassword.
+!
+
+port
+	^ port
+!
+
+port: aNumber
+	port := aNumber
+!
+
+username: aUsername
+	username := aUsername.
+! !
+
+!FileServer methodsFor: 'initialization'!
+
+checkDirectoryLayout
+	(fs existsSync:	(self withBasePath: 'index.html')) ifFalse: [
+		console warn: 'Warning: project directory does not contain index.html.'.
+		console warn: '    You can specify the directory containing index.html with --base-path.'.
+		console warn: '    You can also specify a page to be served by default,'.
+		console warn: '    for all paths that do not map to a file, with --fallback-page.'].
+!
+
+initialize
+	super initialize.
+	path := self require: 'path'.
+	http := self require: 'http'.
+	fs := self require: 'fs'.
+	util := self require: 'util'.
+	url := self require: 'url'.
+	host := self class defaultHost.
+	port := self class defaultPort.
+	username := nil.
+	password := nil.
+	fallbackPage := nil.
+! !
+
+!FileServer methodsFor: 'private'!
+
+base64Decode: aString
+	<return (new Buffer(aString, 'base64').toString())>
+!
+
+isAuthenticated: aRequest
+	"Basic HTTP Auth: http://stackoverflow.com/a/5957629/293175
+	 and https://gist.github.com/1686663"
+	| header token auth parts|
+
+	(username isNil and: [password isNil]) ifTrue: [^ true].
+
+	"get authentication header"
+	header := (aRequest headers at: 'authorization') ifNil:[''].
+	(header isEmpty)
+	ifTrue: [^ false]
+	ifFalse: [
+		"get authentication token"
+		token := (header tokenize: ' ') ifNil:[''].
+		"convert back from base64"
+		auth := self base64Decode: (token at: 2).
+		"split token at colon"
+		parts := auth tokenize: ':'.
+
+		((username = (parts at: 1)) and: [password = (parts at: 2)])
+			ifTrue: [^ true]
+			ifFalse: [^ false]
+	].
+!
+
+require: aModuleString
+	"call to the require function"
+	^require value: aModuleString
+!
+
+validateBasePath
+	"The basePath must be an existing directory. "
+	fs stat: self basePath then: [ :err :stat | err
+		ifNil: [ stat isDirectory ifFalse: [ console warn: 'Warning: --base-path parameter ' , self basePath , ' is not a directory.' ]]
+		ifNotNil: [ console warn: 'Warning: path at --base-path parameter ' , self basePath , ' does not exist.'  ]].
+!
+
+withBasePath: aBaseRelativePath
+	"return a file path which is relative to the basePath."
+	^ path join: self basePath with: aBaseRelativePath
+!
+
+writeData: data toFileNamed: aFilename
+	console log: aFilename
+! !
+
+!FileServer methodsFor: 'request handling'!
+
+handleGETRequest: aRequest respondTo: aResponse
+	| uri filename |
+	uri := url parse: aRequest url.
+	filename := path join: self basePath with: uri pathname.
+	fs exists: filename do: [:aBoolean |
+		aBoolean
+			ifFalse: [self respondNotFoundTo: aResponse]
+			ifTrue: [(fs statSync: filename) isDirectory
+				ifTrue: [self respondDirectoryNamed: filename from: uri to: aResponse]
+				ifFalse: [self respondFileNamed: filename to: aResponse]]]
+!
+
+handleOPTIONSRequest: aRequest respondTo: aResponse
+	aResponse writeHead: 200 options: #{'Access-Control-Allow-Origin' -> '*'.
+					'Access-Control-Allow-Methods' -> 'GET, PUT, POST, DELETE, OPTIONS'.
+					'Access-Control-Allow-Headers' -> 'Content-Type, Accept'.
+					'Content-Length' -> 0.
+					'Access-Control-Max-Age' -> 10}.
+	aResponse end
+!
+
+handlePUTRequest: aRequest respondTo: aResponse
+	| file stream |
+	(self isAuthenticated: aRequest)
+		ifFalse: [self respondAuthenticationRequiredTo: aResponse. ^ nil].
+
+	file := '.', aRequest url.
+	stream := fs createWriteStream: file.
+
+	stream on: 'error' do: [:error |
+		console warn: 'Error creating WriteStream for file ', file.
+		console warn: '    Did you forget to create the necessary directory in your project (often /src)?'.
+		console warn: '    The exact error is: ', error.
+		self respondNotCreatedTo: aResponse].
+
+	stream on: 'close' do: [
+		self respondCreatedTo: aResponse].
+
+	aRequest setEncoding: 'utf8'.
+	aRequest on: 'data' do: [:data |
+		stream write: data].
+
+	aRequest on: 'end' do: [
+		stream writable ifTrue: [stream end]]
+!
+
+handleRequest: aRequest respondTo: aResponse
+	aRequest method = 'PUT'
+		ifTrue: [self handlePUTRequest: aRequest respondTo: aResponse].
+	aRequest method = 'GET'
+		ifTrue:[self handleGETRequest: aRequest respondTo: aResponse].
+	aRequest method = 'OPTIONS'
+		ifTrue:[self handleOPTIONSRequest: aRequest respondTo: aResponse]
+!
+
+respondAuthenticationRequiredTo: aResponse
+	aResponse
+		writeHead: 401 options: #{'WWW-Authenticate' -> 'Basic realm="Secured Developer Area"'};
+		write: '<html><body>Authentication needed</body></html>';
+		end.
+!
+
+respondCreatedTo: aResponse
+	aResponse
+		writeHead: 201 options: #{'Content-Type' -> 'text/plain'. 'Access-Control-Allow-Origin' -> '*'};
+		end.
+!
+
+respondDirectoryNamed: aDirname from: aUrl to: aResponse
+	(aUrl pathname endsWith: '/')
+		ifTrue: [self respondFileNamed: aDirname, 'index.html' to: aResponse]
+		ifFalse: [self respondRedirect: aUrl pathname, '/', (aUrl search ifNil: ['']) to: aResponse]
+!
+
+respondFileNamed: aFilename to: aResponse
+	| type filename |
+
+	filename := aFilename.
+
+	fs readFile: filename do: [:ex :file |
+		ex notNil 
+			ifTrue: [
+				console log: filename, ' does not exist'.
+				self respondNotFoundTo: aResponse]
+			ifFalse: [
+				type := self class mimeTypeFor: filename.
+				type = 'application/javascript'
+					ifTrue: [ type:=type,';charset=utf-8' ].
+				aResponse 
+					writeHead: 200 options:  #{'Content-Type' -> type};
+					write: file encoding: 'binary';
+					end]]
+!
+
+respondInternalErrorTo: aResponse
+	aResponse 
+		writeHead: 500 options: #{'Content-Type' -> 'text/plain'};
+		write: '500 Internal server error';
+		end
+!
+
+respondNotCreatedTo: aResponse
+	aResponse
+		writeHead: 400 options: #{'Content-Type' -> 'text/plain'};
+		write: 'File could not be created. Did you forget to create the src directory on the server?';
+		end.
+!
+
+respondNotFoundTo: aResponse
+	self fallbackPage isNil ifFalse: [^self respondFileNamed: self fallbackPage to: aResponse].
+	aResponse 
+		writeHead: 404 options: #{'Content-Type' -> 'text/html'};
+		write: '<html><body><p>404 Not found</p>';
+		write: '<p>Did you forget to put an index.html file into the directory which is served by "bin/amber serve"? To solve this you can:<ul>';
+		write: '<li>create an index.html in the served directory.</li>';
+		write: '<li>can also specify the location of a page to be served whenever path does not resolve to a file with the "--fallback-page" option.</li>';
+		write: '<li>change the directory to be served with the "--base-path" option.</li>';
+		write: '</ul></p></body></html>';
+		end
+!
+
+respondOKTo: aResponse
+	aResponse
+		writeHead: 200 options: #{'Content-Type' -> 'text/plain'. 'Access-Control-Allow-Origin' -> '*'};
+		end.
+!
+
+respondRedirect: aString to: aResponse
+	aResponse
+		writeHead: 303 options: #{'Location' -> aString};
+		end.
+! !
+
+!FileServer methodsFor: 'starting'!
+
+start
+	"Checks if required directory layout is present (issue warning if not).
+	 Afterwards start the server."
+	self checkDirectoryLayout.
+	(http createServer: [:request :response |
+	      self handleRequest: request respondTo: response])
+	      on: 'error' do: [:error | console log: 'Error starting server: ', error];
+	      on: 'listening' do: [console log: 'Starting file server on http://', self host, ':', self port asString];
+	      listen: self port host: self host.
+!
+
+startOn: aPort
+	self port: aPort.
+	self start
+! !
+
+FileServer class instanceVariableNames: 'mimeTypes'!
+
+!FileServer class methodsFor: 'accessing'!
+
+commandLineSwitches
+	"Collect all methodnames from the 'accessing' protocol
+	 and select the ones with only one parameter.
+	 Then remove the ':' at the end of the name
+	 and add a '--' at the beginning.
+	 Additionally all uppercase letters are made lowercase and preceded by a '-'.
+	 Example: fallbackPage: becomes --fallback-page.
+	 Return the Array containing the commandline switches."
+	| switches |
+	switches := ((self methodsInProtocol: 'accessing') collect: [ :each | each selector]).
+	switches := switches select: [ :each | each match: '^[^:]*:$'].
+	switches :=switches collect: [ :each |
+		(each allButLast replace: '([A-Z])' with: '-$1') asLowercase replace: '^([a-z])' with: '--$1' ].
+	^ switches
+!
+
+defaultBasePath
+	^ './'
+!
+
+defaultHost
+	^ '127.0.0.1'
+!
+
+defaultMimeTypes
+	^ #{
+		'%' -> 'application/x-trash'.
+		'323' -> 'text/h323'.
+		'abw' -> 'application/x-abiword'.
+		'ai' -> 'application/postscript'.
+		'aif' -> 'audio/x-aiff'.
+		'aifc' -> 'audio/x-aiff'.
+		'aiff' -> 'audio/x-aiff'.
+		'alc' -> 'chemical/x-alchemy'.
+		'art' -> 'image/x-jg'.
+		'asc' -> 'text/plain'.
+		'asf' -> 'video/x-ms-asf'.
+		'asn' -> 'chemical/x-ncbi-asn1-spec'.
+		'aso' -> 'chemical/x-ncbi-asn1-binary'.
+		'asx' -> 'video/x-ms-asf'.
+		'au' -> 'audio/basic'.
+		'avi' -> 'video/x-msvideo'.
+		'b' -> 'chemical/x-molconn-Z'.
+		'bak' -> 'application/x-trash'.
+		'bat' -> 'application/x-msdos-program'.
+		'bcpio' -> 'application/x-bcpio'.
+		'bib' -> 'text/x-bibtex'.
+		'bin' -> 'application/octet-stream'.
+		'bmp' -> 'image/x-ms-bmp'.
+		'book' -> 'application/x-maker'.
+		'bsd' -> 'chemical/x-crossfire'.
+		'c' -> 'text/x-csrc'.
+		'c++' -> 'text/x-c++src'.
+		'c3d' -> 'chemical/x-chem3d'.
+		'cac' -> 'chemical/x-cache'.
+		'cache' -> 'chemical/x-cache'.
+		'cascii' -> 'chemical/x-cactvs-binary'.
+		'cat' -> 'application/vnd.ms-pki.seccat'.
+		'cbin' -> 'chemical/x-cactvs-binary'.
+		'cc' -> 'text/x-c++src'.
+		'cdf' -> 'application/x-cdf'.
+		'cdr' -> 'image/x-coreldraw'.
+		'cdt' -> 'image/x-coreldrawtemplate'.
+		'cdx' -> 'chemical/x-cdx'.
+		'cdy' -> 'application/vnd.cinderella'.
+		'cef' -> 'chemical/x-cxf'.
+		'cer' -> 'chemical/x-cerius'.
+		'chm' -> 'chemical/x-chemdraw'.
+		'chrt' -> 'application/x-kchart'.
+		'cif' -> 'chemical/x-cif'.
+		'class' -> 'application/java-vm'.
+		'cls' -> 'text/x-tex'.
+		'cmdf' -> 'chemical/x-cmdf'.
+		'cml' -> 'chemical/x-cml'.
+		'cod' -> 'application/vnd.rim.cod'.
+		'com' -> 'application/x-msdos-program'.
+		'cpa' -> 'chemical/x-compass'.
+		'cpio' -> 'application/x-cpio'.
+		'cpp' -> 'text/x-c++src'.
+		'cpt' -> 'image/x-corelphotopaint'.
+		'crl' -> 'application/x-pkcs7-crl'.
+		'crt' -> 'application/x-x509-ca-cert'.
+		'csf' -> 'chemical/x-cache-csf'.
+		'csh' -> 'text/x-csh'.
+		'csm' -> 'chemical/x-csml'.
+		'csml' -> 'chemical/x-csml'.
+		'css' -> 'text/css'.
+		'csv' -> 'text/comma-separated-values'.
+		'ctab' -> 'chemical/x-cactvs-binary'.
+		'ctx' -> 'chemical/x-ctx'.
+		'cu' -> 'application/cu-seeme'.
+		'cub' -> 'chemical/x-gaussian-cube'.
+		'cxf' -> 'chemical/x-cxf'.
+		'cxx' -> 'text/x-c++src'.
+		'dat' -> 'chemical/x-mopac-input'.
+		'dcr' -> 'application/x-director'.
+		'deb' -> 'application/x-debian-package'.
+		'dif' -> 'video/dv'.
+		'diff' -> 'text/plain'.
+		'dir' -> 'application/x-director'.
+		'djv' -> 'image/vnd.djvu'.
+		'djvu' -> 'image/vnd.djvu'.
+		'dl' -> 'video/dl'.
+		'dll' -> 'application/x-msdos-program'.
+		'dmg' -> 'application/x-apple-diskimage'.
+		'dms' -> 'application/x-dms'.
+		'doc' -> 'application/msword'.
+		'dot' -> 'application/msword'.
+		'dv' -> 'video/dv'.
+		'dvi' -> 'application/x-dvi'.
+		'dx' -> 'chemical/x-jcamp-dx'.
+		'dxr' -> 'application/x-director'.
+		'emb' -> 'chemical/x-embl-dl-nucleotide'.
+		'embl' -> 'chemical/x-embl-dl-nucleotide'.
+		'ent' -> 'chemical/x-pdb'.
+		'eps' -> 'application/postscript'.
+		'etx' -> 'text/x-setext'.
+		'exe' -> 'application/x-msdos-program'.
+		'ez' -> 'application/andrew-inset'.
+		'fb' -> 'application/x-maker'.
+		'fbdoc' -> 'application/x-maker'.
+		'fch' -> 'chemical/x-gaussian-checkpoint'.
+		'fchk' -> 'chemical/x-gaussian-checkpoint'.
+		'fig' -> 'application/x-xfig'.
+		'flac' -> 'application/x-flac'.
+		'fli' -> 'video/fli'.
+		'fm' -> 'application/x-maker'.
+		'frame' -> 'application/x-maker'.
+		'frm' -> 'application/x-maker'.
+		'gal' -> 'chemical/x-gaussian-log'.
+		'gam' -> 'chemical/x-gamess-input'.
+		'gamin' -> 'chemical/x-gamess-input'.
+		'gau' -> 'chemical/x-gaussian-input'.
+		'gcd' -> 'text/x-pcs-gcd'.
+		'gcf' -> 'application/x-graphing-calculator'.
+		'gcg' -> 'chemical/x-gcg8-sequence'.
+		'gen' -> 'chemical/x-genbank'.
+		'gf' -> 'application/x-tex-gf'.
+		'gif' -> 'image/gif'.
+		'gjc' -> 'chemical/x-gaussian-input'.
+		'gjf' -> 'chemical/x-gaussian-input'.
+		'gl' -> 'video/gl'.
+		'gnumeric' -> 'application/x-gnumeric'.
+		'gpt' -> 'chemical/x-mopac-graph'.
+		'gsf' -> 'application/x-font'.
+		'gsm' -> 'audio/x-gsm'.
+		'gtar' -> 'application/x-gtar'.
+		'h' -> 'text/x-chdr'.
+		'h++' -> 'text/x-c++hdr'.
+		'hdf' -> 'application/x-hdf'.
+		'hh' -> 'text/x-c++hdr'.
+		'hin' -> 'chemical/x-hin'.
+		'hpp' -> 'text/x-c++hdr'.
+		'hqx' -> 'application/mac-binhex40'.
+		'hs' -> 'text/x-haskell'.
+		'hta' -> 'application/hta'.
+		'htc' -> 'text/x-component'.
+		'htm' -> 'text/html'.
+		'html' -> 'text/html'.
+		'hxx' -> 'text/x-c++hdr'.
+		'ica' -> 'application/x-ica'.
+		'ice' -> 'x-conference/x-cooltalk'.
+		'ico' -> 'image/x-icon'.
+		'ics' -> 'text/calendar'.
+		'icz' -> 'text/calendar'.
+		'ief' -> 'image/ief'.
+		'iges' -> 'model/iges'.
+		'igs' -> 'model/iges'.
+		'iii' -> 'application/x-iphone'.
+		'inp' -> 'chemical/x-gamess-input'.
+		'ins' -> 'application/x-internet-signup'.
+		'iso' -> 'application/x-iso9660-image'.
+		'isp' -> 'application/x-internet-signup'.
+		'ist' -> 'chemical/x-isostar'.
+		'istr' -> 'chemical/x-isostar'.
+		'jad' -> 'text/vnd.sun.j2me.app-descriptor'.
+		'jar' -> 'application/java-archive'.
+		'java' -> 'text/x-java'.
+		'jdx' -> 'chemical/x-jcamp-dx'.
+		'jmz' -> 'application/x-jmol'.
+		'jng' -> 'image/x-jng'.
+		'jnlp' -> 'application/x-java-jnlp-file'.
+		'jpe' -> 'image/jpeg'.
+		'jpeg' -> 'image/jpeg'.
+		'jpg' -> 'image/jpeg'.
+		'js' -> 'application/javascript'.
+		'kar' -> 'audio/midi'.
+		'key' -> 'application/pgp-keys'.
+		'kil' -> 'application/x-killustrator'.
+		'kin' -> 'chemical/x-kinemage'.
+		'kpr' -> 'application/x-kpresenter'.
+		'kpt' -> 'application/x-kpresenter'.
+		'ksp' -> 'application/x-kspread'.
+		'kwd' -> 'application/x-kword'.
+		'kwt' -> 'application/x-kword'.
+		'latex' -> 'application/x-latex'.
+		'lha' -> 'application/x-lha'.
+		'lhs' -> 'text/x-literate-haskell'.
+		'lsf' -> 'video/x-la-asf'.
+		'lsx' -> 'video/x-la-asf'.
+		'ltx' -> 'text/x-tex'.
+		'lzh' -> 'application/x-lzh'.
+		'lzx' -> 'application/x-lzx'.
+		'm3u' -> 'audio/x-mpegurl'.
+		'm4a' -> 'audio/mpeg'.
+		'maker' -> 'application/x-maker'.
+		'man' -> 'application/x-troff-man'.
+		'mcif' -> 'chemical/x-mmcif'.
+		'mcm' -> 'chemical/x-macmolecule'.
+		'mdb' -> 'application/msaccess'.
+		'me' -> 'application/x-troff-me'.
+		'mesh' -> 'model/mesh'.
+		'mid' -> 'audio/midi'.
+		'midi' -> 'audio/midi'.
+		'mif' -> 'application/x-mif'.
+		'mm' -> 'application/x-freemind'.
+		'mmd' -> 'chemical/x-macromodel-input'.
+		'mmf' -> 'application/vnd.smaf'.
+		'mml' -> 'text/mathml'.
+		'mmod' -> 'chemical/x-macromodel-input'.
+		'mng' -> 'video/x-mng'.
+		'moc' -> 'text/x-moc'.
+		'mol' -> 'chemical/x-mdl-molfile'.
+		'mol2' -> 'chemical/x-mol2'.
+		'moo' -> 'chemical/x-mopac-out'.
+		'mop' -> 'chemical/x-mopac-input'.
+		'mopcrt' -> 'chemical/x-mopac-input'.
+		'mov' -> 'video/quicktime'.
+		'movie' -> 'video/x-sgi-movie'.
+		'mp2' -> 'audio/mpeg'.
+		'mp3' -> 'audio/mpeg'.
+		'mp4' -> 'video/mp4'.
+		'mpc' -> 'chemical/x-mopac-input'.
+		'mpe' -> 'video/mpeg'.
+		'mpeg' -> 'video/mpeg'.
+		'mpega' -> 'audio/mpeg'.
+		'mpg' -> 'video/mpeg'.
+		'mpga' -> 'audio/mpeg'.
+		'ms' -> 'application/x-troff-ms'.
+		'msh' -> 'model/mesh'.
+		'msi' -> 'application/x-msi'.
+		'mvb' -> 'chemical/x-mopac-vib'.
+		'mxu' -> 'video/vnd.mpegurl'.
+		'nb' -> 'application/mathematica'.
+		'nc' -> 'application/x-netcdf'.
+		'nwc' -> 'application/x-nwc'.
+		'o' -> 'application/x-object'.
+		'oda' -> 'application/oda'.
+		'odb' -> 'application/vnd.oasis.opendocument.database'.
+		'odc' -> 'application/vnd.oasis.opendocument.chart'.
+		'odf' -> 'application/vnd.oasis.opendocument.formula'.
+		'odg' -> 'application/vnd.oasis.opendocument.graphics'.
+		'odi' -> 'application/vnd.oasis.opendocument.image'.
+		'odm' -> 'application/vnd.oasis.opendocument.text-master'.
+		'odp' -> 'application/vnd.oasis.opendocument.presentation'.
+		'ods' -> 'application/vnd.oasis.opendocument.spreadsheet'.
+		'odt' -> 'application/vnd.oasis.opendocument.text'.
+		'ogg' -> 'application/ogg'.
+		'old' -> 'application/x-trash'.
+		'oth' -> 'application/vnd.oasis.opendocument.text-web'.
+		'oza' -> 'application/x-oz-application'.
+		'p' -> 'text/x-pascal'.
+		'p7r' -> 'application/x-pkcs7-certreqresp'.
+		'pac' -> 'application/x-ns-proxy-autoconfig'.
+		'pas' -> 'text/x-pascal'.
+		'pat' -> 'image/x-coreldrawpattern'.
+		'pbm' -> 'image/x-portable-bitmap'.
+		'pcf' -> 'application/x-font'.
+		'pcf.Z' -> 'application/x-font'.
+		'pcx' -> 'image/pcx'.
+		'pdb' -> 'chemical/x-pdb'.
+		'pdf' -> 'application/pdf'.
+		'pfa' -> 'application/x-font'.
+		'pfb' -> 'application/x-font'.
+		'pgm' -> 'image/x-portable-graymap'.
+		'pgn' -> 'application/x-chess-pgn'.
+		'pgp' -> 'application/pgp-signature'.
+		'pk' -> 'application/x-tex-pk'.
+		'pl' -> 'text/x-perl'.
+		'pls' -> 'audio/x-scpls'.
+		'pm' -> 'text/x-perl'.
+		'png' -> 'image/png'.
+		'pnm' -> 'image/x-portable-anymap'.
+		'pot' -> 'text/plain'.
+		'ppm' -> 'image/x-portable-pixmap'.
+		'pps' -> 'application/vnd.ms-powerpoint'.
+		'ppt' -> 'application/vnd.ms-powerpoint'.
+		'prf' -> 'application/pics-rules'.
+		'prt' -> 'chemical/x-ncbi-asn1-ascii'.
+		'ps' -> 'application/postscript'.
+		'psd' -> 'image/x-photoshop'.
+		'psp' -> 'text/x-psp'.
+		'py' -> 'text/x-python'.
+		'pyc' -> 'application/x-python-code'.
+		'pyo' -> 'application/x-python-code'.
+		'qt' -> 'video/quicktime'.
+		'qtl' -> 'application/x-quicktimeplayer'.
+		'ra' -> 'audio/x-realaudio'.
+		'ram' -> 'audio/x-pn-realaudio'.
+		'rar' -> 'application/rar'.
+		'ras' -> 'image/x-cmu-raster'.
+		'rd' -> 'chemical/x-mdl-rdfile'.
+		'rdf' -> 'application/rdf+xml'.
+		'rgb' -> 'image/x-rgb'.
+		'rm' -> 'audio/x-pn-realaudio'.
+		'roff' -> 'application/x-troff'.
+		'ros' -> 'chemical/x-rosdal'.
+		'rpm' -> 'application/x-redhat-package-manager'.
+		'rss' -> 'application/rss+xml'.
+		'rtf' -> 'text/rtf'.
+		'rtx' -> 'text/richtext'.
+		'rxn' -> 'chemical/x-mdl-rxnfile'.
+		'sct' -> 'text/scriptlet'.
+		'sd' -> 'chemical/x-mdl-sdfile'.
+		'sd2' -> 'audio/x-sd2'.
+		'sda' -> 'application/vnd.stardivision.draw'.
+		'sdc' -> 'application/vnd.stardivision.calc'.
+		'sdd' -> 'application/vnd.stardivision.impress'.
+		'sdf' -> 'chemical/x-mdl-sdfile'.
+		'sdp' -> 'application/vnd.stardivision.impress'.
+		'sdw' -> 'application/vnd.stardivision.writer'.
+		'ser' -> 'application/java-serialized-object'.
+		'sgf' -> 'application/x-go-sgf'.
+		'sgl' -> 'application/vnd.stardivision.writer-global'.
+		'sh' -> 'text/x-sh'.
+		'shar' -> 'application/x-shar'.
+		'shtml' -> 'text/html'.
+		'sid' -> 'audio/prs.sid'.
+		'sik' -> 'application/x-trash'.
+		'silo' -> 'model/mesh'.
+		'sis' -> 'application/vnd.symbian.install'.
+		'sit' -> 'application/x-stuffit'.
+		'skd' -> 'application/x-koan'.
+		'skm' -> 'application/x-koan'.
+		'skp' -> 'application/x-koan'.
+		'skt' -> 'application/x-koan'.
+		'smf' -> 'application/vnd.stardivision.math'.
+		'smi' -> 'application/smil'.
+		'smil' -> 'application/smil'.
+		'snd' -> 'audio/basic'.
+		'spc' -> 'chemical/x-galactic-spc'.
+		'spl' -> 'application/x-futuresplash'.
+		'src' -> 'application/x-wais-source'.
+		'stc' -> 'application/vnd.sun.xml.calc.template'.
+		'std' -> 'application/vnd.sun.xml.draw.template'.
+		'sti' -> 'application/vnd.sun.xml.impress.template'.
+		'stl' -> 'application/vnd.ms-pki.stl'.
+		'stw' -> 'application/vnd.sun.xml.writer.template'.
+		'sty' -> 'text/x-tex'.
+		'sv4cpio' -> 'application/x-sv4cpio'.
+		'sv4crc' -> 'application/x-sv4crc'.
+		'svg' -> 'image/svg+xml'.
+		'svgz' -> 'image/svg+xml'.
+		'sw' -> 'chemical/x-swissprot'.
+		'swf' -> 'application/x-shockwave-flash'.
+		'swfl' -> 'application/x-shockwave-flash'.
+		'sxc' -> 'application/vnd.sun.xml.calc'.
+		'sxd' -> 'application/vnd.sun.xml.draw'.
+		'sxg' -> 'application/vnd.sun.xml.writer.global'.
+		'sxi' -> 'application/vnd.sun.xml.impress'.
+		'sxm' -> 'application/vnd.sun.xml.math'.
+		'sxw' -> 'application/vnd.sun.xml.writer'.
+		't' -> 'application/x-troff'.
+		'tar' -> 'application/x-tar'.
+		'taz' -> 'application/x-gtar'.
+		'tcl' -> 'text/x-tcl'.
+		'tex' -> 'text/x-tex'.
+		'texi' -> 'application/x-texinfo'.
+		'texinfo' -> 'application/x-texinfo'.
+		'text' -> 'text/plain'.
+		'tgf' -> 'chemical/x-mdl-tgf'.
+		'tgz' -> 'application/x-gtar'.
+		'tif' -> 'image/tiff'.
+		'tiff' -> 'image/tiff'.
+		'tk' -> 'text/x-tcl'.
+		'tm' -> 'text/texmacs'.
+		'torrent' -> 'application/x-bittorrent'.
+		'tr' -> 'application/x-troff'.
+		'ts' -> 'text/texmacs'.
+		'tsp' -> 'application/dsptype'.
+		'tsv' -> 'text/tab-separated-values'.
+		'txt' -> 'text/plain'.
+		'udeb' -> 'application/x-debian-package'.
+		'uls' -> 'text/iuls'.
+		'ustar' -> 'application/x-ustar'.
+		'val' -> 'chemical/x-ncbi-asn1-binary'.
+		'vcd' -> 'application/x-cdlink'.
+		'vcf' -> 'text/x-vcard'.
+		'vcs' -> 'text/x-vcalendar'.
+		'vmd' -> 'chemical/x-vmd'.
+		'vms' -> 'chemical/x-vamas-iso14976'.
+		'vor' -> 'application/vnd.stardivision.writer'.
+		'vrm' -> 'x-world/x-vrml'.
+		'vrml' -> 'x-world/x-vrml'.
+		'vsd' -> 'application/vnd.visio'.
+		'wad' -> 'application/x-doom'.
+		'wav' -> 'audio/x-wav'.
+		'wax' -> 'audio/x-ms-wax'.
+		'wbmp' -> 'image/vnd.wap.wbmp'.
+		'wbxml' -> 'application/vnd.wap.wbxml'.
+		'wk' -> 'application/x-123'.
+		'wm' -> 'video/x-ms-wm'.
+		'wma' -> 'audio/x-ms-wma'.
+		'wmd' -> 'application/x-ms-wmd'.
+		'wml' -> 'text/vnd.wap.wml'.
+		'wmlc' -> 'application/vnd.wap.wmlc'.
+		'wmls' -> 'text/vnd.wap.wmlscript'.
+		'wmlsc' -> 'application/vnd.wap.wmlscriptc'.
+		'wmv' -> 'video/x-ms-wmv'.
+		'wmx' -> 'video/x-ms-wmx'.
+		'wmz' -> 'application/x-ms-wmz'.
+		'wp5' -> 'application/wordperfect5.1'.
+		'wpd' -> 'application/wordperfect'.
+		'wrl' -> 'x-world/x-vrml'.
+		'wsc' -> 'text/scriptlet'.
+		'wvx' -> 'video/x-ms-wvx'.
+		'wz' -> 'application/x-wingz'.
+		'xbm' -> 'image/x-xbitmap'.
+		'xcf' -> 'application/x-xcf'.
+		'xht' -> 'application/xhtml+xml'.
+		'xhtml' -> 'application/xhtml+xml'.
+		'xlb' -> 'application/vnd.ms-excel'.
+		'xls' -> 'application/vnd.ms-excel'.
+		'xlt' -> 'application/vnd.ms-excel'.
+		'xml' -> 'application/xml'.
+		'xpi' -> 'application/x-xpinstall'.
+		'xpm' -> 'image/x-xpixmap'.
+		'xsl' -> 'application/xml'.
+		'xtel' -> 'chemical/x-xtel'.
+		'xul' -> 'application/vnd.mozilla.xul+xml'.
+		'xwd' -> 'image/x-xwindowdump'.
+		'xyz' -> 'chemical/x-xyz'.
+		'zip' -> 'application/zip'.
+		'zmt' -> 'chemical/x-mopac-input'.
+		'~' -> 'application/x-trash'
+	}
+!
+
+defaultPort
+	^ 4000
+!
+
+mimeTypeFor: aString
+	^ self mimeTypes at: (aString replace: '.*[\.]' with: '') ifAbsent: ['text/plain']
+!
+
+mimeTypes
+	^ mimeTypes ifNil: [mimeTypes := self defaultMimeTypes]
+!
+
+printHelp
+	console log: 'Available commandline options are:'.
+	console log: '--help'.
+	self commandLineSwitches do: [ :each |
+		console log: each, ' <parameter>']
+!
+
+selectorForCommandLineSwitch: aSwitch
+	"Remove the trailing '--', add ':' at the end
+	 and replace all occurences of a lowercase letter preceded by a '-' with
+	 the Uppercase letter.
+	 Example: --fallback-page becomes fallbackPage:"
+	^ ((aSwitch replace: '^--' with: '')
+		replace: '-[a-z]' with: [ :each | each second asUppercase ]), ':'
+! !
+
+!FileServer class methodsFor: 'initialization'!
+
+createServerWithArguments: options
+	"If options are empty return a default FileServer instance.
+	 If options are given loop through them and set the passed in values
+	 on the FileServer instance.
+	 
+	 Commanline options map directly to methods in the 'accessing' protocol
+	 taking one parameter.
+	 Adding a method to this protocol makes it directly settable through
+	 command line options.
+	 "
+	| server popFront front optionName optionValue switches |
+
+	switches := self commandLineSwitches.
+
+	server := self new.
+
+	options ifEmpty: [^server].
+
+	(options size even) ifFalse: [
+		console log: 'Using default parameters.'.
+		console log: 'Wrong commandline options or not enough arguments for: ' , options.
+		console log: 'Use any of the following ones: ', switches.
+		^server].
+
+	popFront := [:args |
+		front := args first.
+		args remove: front.
+		front].
+
+	[options notEmpty] whileTrue: [
+		optionName  := popFront value: options.
+		optionValue := popFront value: options.
+
+		(switches includes: optionName) ifTrue: [
+			optionName := self selectorForCommandLineSwitch: optionName.
+			server perform: optionName withArguments: (Array with: optionValue)]
+			ifFalse: [
+				console log: optionName, ' is not a valid commandline option'.
+				console log: 'Use any of the following ones: ', switches ]].
+	^ server.
+!
+
+main
+	"Main entry point for Amber applications.
+	 Creates and starts a FileServer instance."
+	| fileServer args |
+	args := process argv.
+	"Remove the first args which contain the path to the node executable and the script file."
+	args removeFrom: 1 to: 3.
+
+	args detect: [ :each |
+		(each = '--help') ifTrue: [FileServer printHelp]]
+	ifNone: [
+		fileServer := FileServer createServerWithArguments: args.
+		^ fileServer start]
+! !
+
+Object subclass: #Initer
+	instanceVariableNames: 'path childProcess nmPath'
+	package: 'AmberCli'!
+
+!Initer methodsFor: 'action'!
+
+bowerInstallThenDo: aBlock
+	| child |
+	child := childProcess
+		exec: (path join: nmPath with: '.bin' with: 'bower'), ' install'
+		thenDo: aBlock.
+	child stdout pipe: process stdout options: #{ 'end' -> false }
+!
+
+gruntInitThenDo: aBlock
+	| child |
+	child := childProcess
+		exec: (path join: nmPath with: '.bin' with: 'grunt-init'), ' ', (((path join: nmPath with: 'grunt-init-amber') replace: '\\' with: '\\') replace: ':' with: '\:')
+		thenDo: aBlock.
+	child stdout pipe: process stdout options: #{ 'end' -> false }.
+	process stdin resume.
+	process stdin pipe: child stdin options: #{ 'end' -> false }
+!
+
+start
+	self gruntInitThenDo: [ :error |
+		error ifNotNil: [ console log: 'grunt-init exec error:'; log: error. process exit ]
+		ifNil: [
+			self bowerInstallThenDo: [ :error2 |
+				error2 ifNotNil: [ console log: 'bower install exec error:'; log: error2 ].
+				process exit ]]]
+! !
+
+!Initer methodsFor: 'initialization'!
+
+initialize
+	super initialize.
+	path := require value: 'path'.
+	childProcess := require value: 'child_process'.
+	nmPath := path join: self rootDirname with: 'node_modules'
+! !
+
+!Initer methodsFor: 'private'!
+
+dirname
+	<return __dirname>
+!
+
+rootDirname
+	^ path join: self dirname with: '..' with: '..'
+! !
+
+Object subclass: #NodeTestRunner
+	instanceVariableNames: ''
+	package: 'AmberCli'!
+
+!NodeTestRunner class methodsFor: 'not yet classified'!
+
+runTestSuite
+	| suite worker |
+
+	suite := OrderedCollection new.
+	(TestCase allSubclasses select: [ :each | each isAbstract not ])
+		do: [ :each | suite addAll: each buildSuite ].
+
+	worker := TestSuiteRunner on: suite.
+	worker announcer on: ResultAnnouncement do:
+		[ :ann | | result |
+			result := ann result.
+			result runs = result total ifTrue: [
+				console log: result runs asString, ' tests run, ', result failures size asString, ' failures, ', result errors size asString, ' errors.'.
+
+				result failures isEmpty ifFalse: [
+					result failures first runCase.
+					"the line above should throw, normally, but just in case I leave the line below"
+					self throw: result failures first class name, ' >> ', result failures first selector, ' is failing!!!!' ].
+				result errors isEmpty ifFalse: [
+					result errors first runCase.
+					"the line above should throw, normally, but just in case I leave the line below"
+					self throw: result errors first class name, ' >> ', result errors first selector, ' has errors!!!!' ].
+	]].
+	worker run
+! !
+
+Object subclass: #Repl
+	instanceVariableNames: 'readline interface util session resultCount commands'
+	package: 'AmberCli'!
+!Repl commentStamp!
+I am a class representing a REPL (Read Evaluate Print Loop) and provide a command line interface to Amber Smalltalk.
+On the prompt you can type Amber statements which will be evaluated after pressing <Enter>.
+The evaluation is comparable with executing a 'DoIt' in a workspace.
+
+My runtime requirement is a functional Node.js executable with working Readline support.!
+
+!Repl methodsFor: 'accessing'!
+
+commands
+	^ commands
+!
+
+prompt
+	^ 'amber >> '
+! !
+
+!Repl methodsFor: 'actions'!
+
+clearScreen
+	| esc cls |
+	esc := String fromCharCode: 27.
+	cls := esc, '[2J', esc, '[0;0f'.
+	process stdout write: cls.
+	interface prompt
+!
+
+close
+	process stdin destroy
+!
+
+createInterface
+	interface := readline createInterface: process stdin stdout: process stdout.
+	interface on: 'line' do: [:buffer | self processLine: buffer].
+	interface on: 'close' do: [self close].
+	self printWelcome; setupHotkeys; setPrompt.
+	interface prompt
+!
+
+eval: buffer
+	^ self eval: buffer on: DoIt new.
+!
+
+eval: buffer on: anObject
+	| result |
+	buffer isEmpty ifFalse: [
+		[result := Compiler new evaluateExpression: buffer on: anObject]
+			tryCatch: [:e |
+				e isSmalltalkError
+				    ifTrue: [ e resignal ]
+			 	   ifFalse: [ process stdout write: e jsStack ]]].
+	^ result
+!
+
+printWelcome
+	Transcript show: 'Type :q to exit.'; cr.
+!
+
+setPrompt
+	interface setPrompt: self prompt
+! !
+
+!Repl methodsFor: 'initialization'!
+
+initialize
+	super initialize.
+	session := DoIt new.
+	readline := require value: 'readline'.
+	util := require value: 'util'.
+	self setupCommands
+!
+
+setupCommands
+	commands := Dictionary from: {
+		{':q'} -> [process exit].
+		{''} -> [interface prompt]}
+!
+
+setupHotkeys
+	process stdin on: 'keypress' do: [:s :key | key ifNotNil: [self onKeyPress: key]].
+! !
+
+!Repl methodsFor: 'private'!
+
+addVariableNamed: aString to: anObject
+	| newClass newObject |
+	newClass := self subclass: anObject class withVariable: aString.
+	self encapsulateVariable: aString withValue: anObject in: newClass.
+	newObject := newClass new.
+	self setPreviousVariablesFor: newObject from: anObject.
+	^ newObject
+!
+
+assignNewVariable: buffer do: aBlock
+	"Assigns a new variable and calls the given block with the variable's name and value
+	 if buffer contains an assignment expression. If it doesn't the block is called with nil for
+	 both arguments."
+	^ self parseAssignment: buffer do: [ :name :expr || varName value |
+		varName := name ifNil: [self nextResultName].
+		session := self addVariableNamed: varName to: session.
+		[ value := self eval: varName, ' := ', (expr ifNil: [buffer]) on: session ]
+			on: Error
+			do: [ :e | ConsoleErrorHandler new logError: e. value := nil].
+		aBlock value: varName value: value]
+!
+
+encapsulateVariable: aString withValue: anObject in: aClass
+	"Add getter and setter for given variable to session."
+	| compiler |
+	compiler := Compiler new.
+	compiler install: aString, ': anObject ^ ', aString, ' := anObject' forClass: aClass protocol: 'session'.
+	compiler install: aString, ' ^ ', aString forClass: aClass protocol: 'session'.
+!
+
+executeCommand: aString
+	"Tries to process the given string as a command. Returns true if it was a command, false if not."
+	self commands keysAndValuesDo: [:names :cmd |
+		(names includes: aString) ifTrue: [
+			cmd value.
+			^ true]].
+	^ false
+!
+
+instanceVariableNamesFor: aClass
+	"Yields all instance variable names for the given class, including inherited ones."
+	^ aClass superclass
+		ifNotNil: [
+			aClass instanceVariableNames copyWithAll: (self instanceVariableNamesFor: aClass superclass)]
+		ifNil: [
+			aClass instanceVariableNames]
+!
+
+isIdentifier: aString
+	^ aString match: '^[a-z_]\w*$' asRegexp
+!
+
+isVariableDefined: aString
+	^ (self instanceVariableNamesFor: session class) includes: aString
+!
+
+nextResultName
+	resultCount := resultCount
+    	ifNotNil: [resultCount + 1]
+    	ifNil: [1].
+    ^ 'res', resultCount asString
+!
+
+onKeyPress: key
+	(key ctrl and: [key name = 'l'])
+		ifTrue: [self clearScreen]
+!
+
+parseAssignment: aString do: aBlock
+	"Assigns a new variable if the given string is an assignment expression. Calls the given block with name and value.
+	 If the string is not one no variable will be assigned and the block will be called with nil for both arguments."
+	| assignment |
+	assignment := (aString tokenize: ':=') collect: [:s | s trimBoth].
+	^ (assignment size = 2 and: [self isIdentifier: assignment first])
+		ifTrue: [ aBlock value: assignment first value: assignment last ]
+		ifFalse: [ aBlock value: nil value: nil ]
+!
+
+presentResultNamed: varName withValue: value
+	Transcript show: varName, ': ', value class name, ' = ', value asString; cr.
+	interface prompt
+!
+
+processLine: buffer
+	"Processes lines entered through the readline interface."
+	| show |
+	show := [:varName :value | self presentResultNamed: varName withValue: value].
+	(self executeCommand: buffer) ifFalse: [
+		(self isVariableDefined: buffer)
+			ifTrue: [show value: buffer value: (session perform: buffer)]
+			ifFalse: [self assignNewVariable: buffer do: show]]
+!
+
+setPreviousVariablesFor: newObject from: oldObject
+	(self instanceVariableNamesFor: oldObject class) do: [:each |
+		newObject perform: each, ':' withArguments: {oldObject perform: each}].
+!
+
+subclass: aClass withVariable: varName
+	"Create subclass with new variable."
+	^ ClassBuilder new
+		addSubclassOf: aClass
+		named: (self subclassNameFor: aClass) asSymbol
+		instanceVariableNames: {varName}
+		package: 'Compiler-Core'
+!
+
+subclassNameFor: aClass
+	^ (aClass name matchesOf: '\d+$')
+		ifNotNil: [ | counter |
+			counter := (aClass name matchesOf: '\d+$') first asNumber + 1.
+			aClass name replaceRegexp: '\d+$' asRegexp with: counter asString]
+		ifNil: [
+			aClass name, '2'].
+! !
+
+!Repl class methodsFor: 'initialization'!
+
+main
+	self new createInterface
+! !
+

File diff suppressed because it is too large
+ 51826 - 0
external/amber-cli/cli/support/amber-cli.js


+ 197 - 0
external/amber-cli/cli/support/amberc-cli.js

@@ -0,0 +1,197 @@
+#!/usr/bin/env node
+
+var path = require('path');
+var amberc = require('amber-dev/lib/amberc');
+
+// get parameters passed to the command line script
+// discard the first two parameters which are the node binary and the script name
+var parameters = process.argv.slice(2);
+
+// check if at least one parameter was passed to the script
+if (1 > parameters.length) {
+	print_usage();
+	process.exit();
+}
+
+
+// Get Amber root directory from the location of this script so that
+// we can find the st and js directories etc.
+var amber_dir = path.normalize(path.join(__dirname, '..', '..', 'node_modules', 'amber'));
+
+var compiler = new amberc.Compiler(amber_dir);
+
+var configuration = handle_options(parameters);
+
+compiler.main(configuration);
+
+
+/**
+ * Process given program options and update defaults values.
+ * Followed by check_for_closure_compiler() and then collect_files().
+ */
+function handle_options(optionsArray) {
+	var programName = [];
+	var currentItem = optionsArray.shift();
+	var defaults = amberc.createDefaultConfiguration();
+
+	while(undefined !== currentItem) {
+		switch(currentItem) {
+			case '-l':
+				defaults.load.push.apply(defaults.load, optionsArray.shift().split(','));
+				break;
+			case '-L':
+				defaults.jsLibraryDirs.push.apply(defaults.jsLibraryDirs, optionsArray.shift().split(','));
+				break;
+			case '-g':
+				defaults.jsGlobals.push.apply(defaults.jsGlobals, optionsArray.shift().split(','));
+				break;
+			case '-m':
+				defaults.main = optionsArray.shift();
+				break;
+			case '-M':
+				defaults.mainfile = optionsArray.shift();
+				break;
+			case '-n':
+				defaults.amd_namespace = optionsArray.shift();
+				break;
+			case '-D':
+				defaults.output_dir = optionsArray.shift();
+				break;
+            case '-d':
+                amber_dir = path.normalize(optionsArray.shift());
+                break;
+			case '-s':
+				defaults.suffix = optionsArray.shift();
+				defaults.suffix_used = defaults.suffix;
+				break;
+			case '-S':
+				defaults.loadsuffix = optionsArray.shift();
+				defaults.suffix_used = defaults.suffix;
+				break;
+			case '-v':
+				defaults.verbose = true;
+				break;
+			case '-h':
+			case '--help':
+			case '?':
+				print_usage();
+				break;
+			default:
+				var fileSuffix = path.extname(currentItem);
+				switch (fileSuffix) {
+					case '.st':
+						defaults.stFiles.push(currentItem);
+						break;
+					case '.js':
+						defaults.jsFiles.push(currentItem);
+						break;
+					default:
+						// Will end up being the last non js/st argument
+						programName.push(currentItem);
+						break;
+				}
+		}
+		currentItem = optionsArray.shift();
+	}
+
+	if(1 < programName.length) {
+		throw new Error('More than one name for ProgramName given: ' + programName);
+	} else {
+		defaults.program = programName[0];
+	}
+	return defaults;
+}
+
+
+// print available flags
+function print_usage() {
+	var usage = [
+		'Usage: amberc [-l lib1,lib2...] [-g jsGlobal1,jsGlobla2] [-m main_class] [-M main_file]',
+		'          [-n namespace] [-D output_dir] [-v] [-s suffix] [-S suffix] [file1 [file2 ...]] [Program]',
+		'',
+		'   amberc compiles Amber files - either separately or into a complete runnable',
+		'   program. If no .st files are listed only a linking stage is performed.',
+		'   Files listed will be handled using the following rules:',
+		'',
+		'   *.js',
+		'     Files are linked (concatenated) in listed order.',
+		'     If not found we look in $AMBER/src/',
+		'',
+		'   *.st',
+		'     Files are compiled into .js files before concatenation.',
+		'     If not found we look in $AMBER/src/',
+		'',
+		'     NOTE: Each .st file is currently considered to be a fileout of a single class',
+		'     category of the same name as the file!',
+		'',
+		'   If no <Program> is specified each given .st file will be compiled into',
+		'   a matching .js file. Otherwise a <Program>.js file is linked together based on',
+		'   the given options:',
+		'',
+		'  -l library1,library2',
+		'     Add listed JavaScript libraries in listed order.',
+		'     Libraries are not separated by spaces or end with .js.',
+		'',
+		'  -L directory1,directory2',
+		'     Add listed directories to the library search path.',
+		'     The order of processing is:',
+		'     1. current directory',
+		'     2. directories specified by -L',
+		'     3. $AMBER',
+		'',
+		'  -g jsGlobal1,jsGlobal2',
+		'     Comma separated list of JS global variable names.',
+		'     The names are added to a list containing "window", "document" and others.',
+		'',
+		'  -m main_class',
+		'     Add a call to the class method main_class>>main at the end of <Program>.',
+		'',
+		'  -M main_file',
+		'     Add <main_file> at the end of <Program.js> acting as #main.',
+		'',
+		'  -n amd_namespace',
+		'     Export packages with <amd_namespace> as the require.js namespace.',
+		'     Default value is "amber_core".',
+		'',
+		'  -v',
+		'     Produce a more verbose output.',
+		'',
+		'  -D',
+		'     Specifies the output directory for all generated .js files.',
+		'     The hierarchy of the input files is not maintaned.',
+		'     If this option is omitted all generated .js files are placed next to their input files',
+		'',
+		'  -d',
+		'     Specifies the alternate directory to look for Amber files.',
+		'     If not specified, the version embedded in CLI is used..',
+		'',
+		'  -s suffix',
+		'     Add <suffix> to compiled .js files. File.st is then compiled into',
+		'     File.<suffix>.js.',
+		'',
+		'  -S suffix',
+		'     Use <suffix> for all libraries accessed using -l. This makes it possible',
+		'     to have multiple flavors of Amber and libraries in the same place.',
+		'',
+		'',
+		'     Example invocations:',
+		'',
+		'     Just compile Kernel-Objects.st to Kernel-Objects.js:',
+		'',
+		'        amberc Kernel-Objects.st',
+		'',
+		'     Compile Hello.st to Hello.js and create complete program called Program.js.',
+		'     Additionally add a call to the class method Hello>>main:',
+		'',
+		'        amberc -m Hello Hello.st Program',
+		'',
+		'     Compile Cat1.st and Cat2.st files into corresponding .js files.',
+		'     Link them with myboot.js and myKernel.js',
+		'     and merge everything into a complete program named Program.js:',
+		'',
+		'        amberc -M main.js myboot.js myKernel.js Cat1.st Cat2.st Program',
+	];
+	usage.forEach(function (line) {
+        console.log(line);
+	});
+}

+ 49 - 0
external/amber-cli/package.json

@@ -0,0 +1,49 @@
+{
+  "name": "amber-cli",
+  "version": "0.0.9",
+  "description": "An implementation of the Smalltalk language that runs on top of the JS runtime.",
+  "homepage": "http://amber-lang.net",
+  "keywords": [
+    "javascript",
+    "smalltalk",
+    "language",
+    "compiler",
+    "web"
+  ],
+  "author": {
+    "name": "Nicolas Petton",
+    "email": "petton.nicolas@gmail.com",
+    "url": "http://www.nicolas-petton.fr"
+  },
+  "license": {
+    "type": "MIT"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git://github.com/amber-smalltalk/amber.git"
+  },
+  "engines": {
+    "node": ">=0.8.0"
+  },
+  "bugs": {
+    "url": "https://github.com/amber-smalltalk/amber/issues?labels=cli"
+  },
+  "bin": {
+    "amber": "./cli/support/amber-cli.js",
+    "amberc": "./cli/support/amberc-cli.js"
+  },
+  "scripts": {
+    "test": "grunt amberc:cli && node ./cli/support/amber-cli.js tests"
+  },
+  "dependencies": {
+    "amber": "~0.12.4",
+    "grunt-init-amber": "git://github.com/amber-smalltalk/grunt-init-amber.git",
+    "grunt-init": "~0.3.1",
+    "bower": "~1.3.2",
+    "amber-dev": "0.0.2"
+  },
+  "devDependencies": {
+    "grunt": "~0.4.0",
+    "grunt-contrib-jshint": "~0.3.0"
+  }
+}

Some files were not shown because too many files changed in this diff