Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add install command #171

Merged
merged 2 commits into from
Aug 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 48 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## Netlify Lambda

This is an optional tool that helps with building or locally developing [Netlify Functions](https://www.netlify.com/docs/functions/) with a simple webpack/babel build step.
This is an optional tool that helps with building or locally developing [Netlify Functions](https://www.netlify.com/docs/functions/) with a simple webpack/babel build step. For function folders, there is also a small utility to install function folder dependencies.

The goal is to make it easy to write Lambda's with transpiled JS/TypeScript features and imported modules.

Expand All @@ -15,9 +15,9 @@ There are 3 ways to deploy functions to Netlify:

`Netlify-Lambda` uses webpack to bundle up your functions and their dependencies for you, suiting the first approach. However, if you have native node modules (or other dependencies that don't expect to be bundled like [the Firebase SDK](https://github.com/netlify/netlify-lambda/issues/112)) then you may want to try the other approaches. In particular, try [`Netlify Dev`](https://github.com/netlify/netlify-dev-plugin#what-is-netlify-dev).

If this sounds confusing, support is available through [our regular channels](https://www.netlify.com/support/).
</details>
If this sounds confusing, support is available through [our regular channels](https://www.netlify.com/support/).

</details>

<details>
<summary><b>Netlify Dev vs. Netlify-Lambda</b></summary>
Expand All @@ -26,16 +26,14 @@ If this sounds confusing, support is available through [our regular channels](ht

[`Netlify Dev`](https://github.com/netlify/netlify-dev-plugin#what-is-netlify-dev) is incrementally adoptable. **`netlify-lambda` is still recommended if you need a build step for your functions**, as explained here:

- **When to use Netlify Dev**: Part of Netlify Dev serves unbundled function folders through [zip-it-and-ship-it](https://github.com/netlify/zip-it-and-ship-it) with no build step. This is likely to be attractive to many users who previously just needed `netlify-lambda` for bundling multi-file functions or functions with node_module dependencies.
- **When to use Netlify Dev**: Part of Netlify Dev serves unbundled function folders through [zip-it-and-ship-it](https://github.com/netlify/zip-it-and-ship-it) with no build step. This is likely to be attractive to many users who previously just needed `netlify-lambda` for bundling multi-file functions or functions with node_module dependencies.
- **When to use Netlify Lambda**: However, if you need a build step for your functions (e.g. for webpack import/export syntax, running babel transforms or typescript), you can use `netlify-lambda`, `tsc` or your own build tool to do this, just point Netlify Dev at your build output with the `functions` field in `netlify.toml`.
- These responsibilities aren't exactly the same. Therefore **you can use Netlify Dev and Netlify Lambda together** to have BOTH a build step for functions from `netlify-lambda` and the full proxy environment from Netlify Dev. If you have a npm script in `package.json` for running `netlify-lambda serve ${functionsSourceFolder}`, Netlify Dev will [detect it](https://github.com/netlify/netlify-dev-plugin#function-builders-function-builder-detection-and-relationship-with-netlify-lambda) and run it for you. This way, **existing `netlify-lambda` users will be able to use Netlify Dev with no change to their workflow**

Function Builder detection is a very new feature with only simple detection logic for now, that we aim to improve over time. If it doesn't work well for you, you can simply not use Netlify Dev for now while we work out all your bug reports. 🙏🏼

</details>



## Installation

**We recommend installing locally** rather than globally:
Expand All @@ -58,13 +56,36 @@ If you don't have a [`netlify.toml`](https://www.netlify.com/docs/netlify-toml-r

## Usage

We expose two commands:
We expose three commands:

```
```bash
netlify-lambda serve <folder>
netlify-lambda build <folder>
netlify-lambda install [folder]
```

### `netlify-lambda install`

Sometimes your function folders will have dependencies unique to them, managed by a package.json local to that folder. This is a small utility function for installing those dependencies either on your local machine or as part of your build commands.

By default it just runs on the functions folder specified in `netlify.toml`:

```bash
netlify-lambda install
```

This is what you should do if you are just using Netlify Dev without `netlify-lambda`.

If you're using `netlify-lambda serve` or `build`, however, you will want to run this install on the _source_ folder rather than the _dist_/netlify.toml functions folder, so you should run it with the same exact folder name as with those other commands:

```bash
netlify-lambda install <folderName>
```

We don't anticipate you will use this as often but it can be handy.

### `netlify-lambda serve` and `netlify-lambda build`

At a high level, `netlify-lambda` takes a source folder (e.g. `src/lambda`, specified in your command) and outputs it to a built folder, (e.g. `built-lambda`, specified in your `netlify.toml` file).

The `build` function will run a single build of the functions in the folder.
Expand Down Expand Up @@ -164,12 +185,12 @@ Say you are running `webpack-serve` on port 8080 and `netlify-lambda serve` on p

```js
module.exports = {
mode: 'development',
mode: "development",
devServer: {
proxy: {
'/.netlify': {
target: 'http://localhost:9000',
pathRewrite: { '^/.netlify/functions': '' }
"/.netlify": {
target: "http://localhost:9000",
pathRewrite: { "^/.netlify/functions": "" }
}
}
}
Expand All @@ -186,7 +207,7 @@ CORS issues when trying to use netlify-lambdas locally with angular? you need to
Firstly make sure you are using relative paths in your app to ensure that your app will work locally and on Netlify, example below...

```js
this.http.get('/.netlify/functions/jokeTypescript');
this.http.get("/.netlify/functions/jokeTypescript");
```

Then place a `proxy.config.json` file in the root of your project, the contents should look something like...
Expand Down Expand Up @@ -240,21 +261,21 @@ yarn add -D http-proxy-middleware express
```js
// server.js
/* eslint-disable no-console */
const express = require('express');
const next = require('next');
const express = require("express");
const next = require("next");

const devProxy = {
'/.netlify': {
target: 'http://localhost:9000',
pathRewrite: { '^/.netlify/functions': '' }
"/.netlify": {
target: "http://localhost:9000",
pathRewrite: { "^/.netlify/functions": "" }
}
};

const port = parseInt(process.env.PORT, 10) || 3000;
const env = process.env.NODE_ENV;
const dev = env !== 'production';
const dev = env !== "production";
const app = next({
dir: '.', // base directory where everything is, could move to src later
dir: ".", // base directory where everything is, could move to src later
dev
});

Expand All @@ -268,14 +289,14 @@ app

// Set up the proxy.
if (dev && devProxy) {
const proxyMiddleware = require('http-proxy-middleware');
const proxyMiddleware = require("http-proxy-middleware");
Object.keys(devProxy).forEach(function(context) {
server.use(proxyMiddleware(context, devProxy[context]));
});
}

// Default catch-all handler to allow Next.js to handle all other routes
server.all('*', (req, res) => handle(req, res));
server.all("*", (req, res) => handle(req, res));

server.listen(port, err => {
if (err) {
Expand All @@ -285,10 +306,9 @@ app
});
})
.catch(err => {
console.log('An error occurred, unable to start the server');
console.log("An error occurred, unable to start the server");
console.log(err);
});

```

run your server and netlify-lambda at the same time:
Expand Down Expand Up @@ -347,9 +367,9 @@ The additional webpack config will be merged into the default config via [webpac

The default webpack configuration uses `babel-loader` with a [few basic settings](https://github.com/netlify/netlify-lambda/blob/master/lib/build.js#L19-L33).

However, if any `.babelrc` is found in the directory `netlify-lambda` is run from, or [folders above it](https://github.com/netlify/netlify-lambda/pull/92) (useful for monorepos), it will be used instead of the default one.
However, if any `.babelrc` is found in the directory `netlify-lambda` is run from, or [folders above it](https://github.com/netlify/netlify-lambda/pull/92) (useful for monorepos), it will be used instead of the default one.

It is possible to disable this behaviour by passing `--babelrc false`.
It is possible to disable this behaviour by passing `--babelrc false`.

If you need to run different babel versions for your lambda and for your app, [check this issue](https://github.com/netlify/netlify-lambda/issues/34) to override your webpack babel-loader.

Expand Down Expand Up @@ -441,7 +461,7 @@ Minor note: For the `identity` field, since we are not fully emulating Netlify I

## Debugging

To debug lambdas, it can be helpful to turn off minification and enable logging. Prepend the `serve` command with [npm's package runner npx](https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b), e.g. `npx --node-arg=--inspect netlify-lambda serve ...`.
To debug lambdas, it can be helpful to turn off minification and enable logging. Prepend the `serve` command with [npm's package runner npx](https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b), e.g. `npx --node-arg=--inspect netlify-lambda serve ...`.

1. make sure that sourcemaps are built along the way (e.g. in the webpack configuration and the `tsconfig.json` if typescript is used)
2. webpack's minification/uglification is turned off (see below):
Expand All @@ -464,7 +484,7 @@ Netlify Functions [run in Node v8.10](https://www.netlify.com/blog/2018/04/03/no
**Special warning on `node-fetch`**: `node-fetch` and webpack [currently don't work well together](https://github.com/bitinn/node-fetch/issues/450). You will have to use the default export in your code:

```js
const fetch = require('node-fetch').default // not require('node-fetch')
const fetch = require("node-fetch").default; // not require('node-fetch')
```

Don't forget to search our issues in case someone has run into a similar problem you have!
Expand Down
36 changes: 26 additions & 10 deletions bin/cmd.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,27 @@ var pkg = JSON.parse(
);
var build = require("../lib/build");
var serve = require("../lib/serve");
var install = require("../lib/install");

program.version(pkg.version);

const stringBooleanToBoolean = val => {
console.log({val});
if (typeof val !== 'string' && (val !== 'true' || val !== 'false')) {
console.log({ val });
if (typeof val !== "string" && (val !== "true" || val !== "false")) {
throw Error(`Incorrect string value: ${val}`);
}

return val === 'true';
return val === "true";
};

program
.option("-c --config <webpack-config>", "additional webpack configuration")
.option("-p --port <port>", "port to serve from (default: 9000)")
.option("-b --babelrc <babelrc>", "use .babelrc in root (default: true)", stringBooleanToBoolean)
.option(
"-b --babelrc <babelrc>",
"use .babelrc in root (default: true)",
stringBooleanToBoolean
)
.option(
"-t --timeout <timeout>",
"function invocation timeout in seconds (default: 10)"
Expand All @@ -47,13 +52,13 @@ program
static,
Number(program.timeout) || 10
);
}
};
if (static) {
startServer();
return; // early terminate, don't build
};
const { config: userWebpackConfig, babelrc: useBabelrc = true} = program;
build.watch(cmd, { userWebpackConfig, useBabelrc}, function(err, stats) {
}
const { config: userWebpackConfig, babelrc: useBabelrc = true } = program;
build.watch(cmd, { userWebpackConfig, useBabelrc }, function(err, stats) {
if (err) {
console.error(err);
return;
Expand All @@ -75,9 +80,9 @@ program
.action(function(cmd, options) {
console.log("netlify-lambda: Building functions");

const { config: userWebpackConfig, babelrc: useBabelrc = true} = program;
const { config: userWebpackConfig, babelrc: useBabelrc = true } = program;
build
.run(cmd, { userWebpackConfig, useBabelrc})
.run(cmd, { userWebpackConfig, useBabelrc })
.then(function(stats) {
console.log(stats.toString({ color: true }));
})
Expand All @@ -87,6 +92,17 @@ program
});
});

program
.command("install [dir]")
.description("install functions")
.action(function(cmd, options) {
console.log("netlify-lambda: installing function dependencies");
install.run(cmd).catch(function(err) {
console.error(err);
process.exit(1);
});
});

// error on unknown commands
// ref: https://github.com/tj/commander.js#custom-event-listeners
program.on("command:*", function() {
Expand Down
38 changes: 38 additions & 0 deletions lib/install.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const path = require("path");
const fs = require("fs");
const globby = require("globby");
const cp = require("child_process");
var conf = require("./config");

function installDeps(functionDir, cb) {
cp.exec("npm i", { cwd: functionDir }, cb);
}

exports.run = async function(dir) {
let directory;
if (dir) {
var dirPath = path.join(process.cwd(), dir);
directory = dirPath;
} else {
var config = conf.load();
const functionsDir = config.build.functions || config.build.Functions;
if (!functionsDir) {
console.log("Error: no functions dir detected.");
}
const functionsPath = path.join(process.cwd(), functionsDir);
directory = functionsPath;
}

const findJSFiles = ["*/package.json", "!node_modules", "!**/node_modules"];
const foldersWithDeps = await globby(findJSFiles, { cwd: directory });

const folders = foldersWithDeps
.map(fnFolder => {
return fnFolder.substring(0, fnFolder.indexOf("package.json"));
})
.map(folder => {
installDeps(path.join(directory, folder), () => {
console.log(`${folder} dependencies installed`);
});
});
};
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"devDependencies": {
"auto-changelog": "^1.13.0",
"gh-release": "^3.5.0",
"globby": "^10.0.1",
"jest": "^23.6.0"
}
}
Loading