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

How to use in Monorepo without producing tons of duplicate code. #430

Open
Tracked by #495
khrome83 opened this issue Aug 2, 2018 · 7 comments
Open
Tracked by #495

How to use in Monorepo without producing tons of duplicate code. #430

khrome83 opened this issue Aug 2, 2018 · 7 comments

Comments

@khrome83
Copy link

khrome83 commented Aug 2, 2018

This is a Question / Clarification

Description

How do you use this within a Monorepo. Ideally you would define a single webpack.config.js and .babelrc file, and no matter the services, it should share that config. It seems when I specify a webpack.config.js in root, it assumes the functions are located there.

What is the best practices? Is this supported?

@rbobko93
Copy link

rbobko93 commented Aug 7, 2018

In order to have multiple functions in different folders you have to add this to your webpack confg:

module.exports = { entry: slsw.lib.entries };

It takes all functions from your serverless config which contains paths to all functions handlers. For example your serverless.yml file can look like this:

functions: trackFulfillment: handler: src/functions/track/fulfillment/handler.trackFulfillment trackValidation: handler: src/functions/track/validation/handler.trackValidation feedbackFulfillment: handler: src/functions/feedback/fulfillment/handler.feedbackFulfillment feedbackValidation: handler: src/functions/feedback/validation/handler.feedbackValidation

When configured like this you don't need to change anything in webpack config whenever new function is added.

@khrome83
Copy link
Author

khrome83 commented Aug 7, 2018

@rbobko93 - I did discover this, but this makes sense if I have one serverless.yml file.

I am looking at a structure more like below. Each services has a package.json and a serverless.yml.

I am curious if each serverless.yml should be configured like something below - What is the best practice. Does each package.json need to have webpack and babel plugin inside of each service, or is there a way to contain this at root to reduce code?

In a traditional node monorepo, I would have a separate package.json in each separated concerns. How do you mimic that structure with the least code copy/pasted between them. Does this make more sense?

My Current Folder Structure

package.json
webpack.config.js
.babelrc
jest.config.js
resources/
  dynamo-table.yml
  s3-bucket.yml
  package.json
  serverless.yml
services/
  users/
    create.js
    create.test.js
    list.js
    list.test.js
    delete.js
    delete.test.js
    update.js
    update.test.js
    get.js
    get.test.js
    package.json
    serverless.yml
  projects/
    create.js
    create.test.js
    list.js
    list.test.js
    delete.js
    delete.test.js
    update.js
    update.test.js
    get.js
    get.test.js
    package.json
    serverless.yml
  etc/
    ...

Example Service Yaml File

service: service-name

plugins:
  - serverless-webpack
  - serverless-dynamodb-local
  - serverless-offline
  - serverless-domain-manager
  - serverless-plugin-stage-variables

custom:
  webpack:
    webpackConfig: ../../webpack.config.js
    includeModules:
      forceExclude:
        - aws-sdk
  stage: ${opt:stage, self:provider.stage}
  stageVariables:
    env: ${self:custom.stage}
  tables:
    projects: projects-${self:custom.stage}
  domains:
    prod: api.example.com
    stage: stage-api.example.com
    dev: dev-api.example.com
  customDomain:
    domainName: ${self:custom.domains.${self:custom.stage}}
    basePath: 'projects'
    stage: ${self:custom.stage}
    createRoute53Record: true
  dynamodb:
    start:
      port: 8000
      inMemory: true
      migrate: true
    migration:
      dir: migrations

package:
  exclude:
  - coverage/**
  - migrations/**
  - .circleci/**
  - .git/**
  - tests/**

provider:
  name: aws
  runtime: nodejs8.10
  stage: dev
  region: us-east-1
  iamRoleStatements:
    - Effect: "Allow"
      Action:
        - dynamodb:Query
        - dynamodb:Scan
        - dynamodb:GetItem
        - dynamodb:PutItem
        - dynamodb:UpdateItem
        - dynamodb:DeleteItem
      Resource: "arn:aws:dynamodb:*:*:table/${self:custom.tables.projects}"

functions:
  create:
    handler: create.default
    events:
      - http:
          path: /
          method: post
          cors: true
    environment:
      PROJECTS_TABLE: ${self:custom.tables.projects}

  list:
    handler: list.default
    events:
      - http:
          path: /
          method: get
          cors: true
    environment:
      PROJECTS_TABLE: ${self:custom.tables.projects}

  get:
    handler: get.default
    events:
      - http:
          path: /{id}
          method: get
          cors: true
    environment:
      PROJECTS_TABLE: ${self:custom.tables.projects}

  update:
    handler: update.default
    events:
      - http:
          path: /{id}
          method: put
          cors: true
    environment:
      PROJECTS_TABLE: ${self:custom.tables.projects}

  delete:
    handler: delete.default
    events:
      - http:
          path: /{id}
          method: delete
          cors: true
    environment:
      PROJECTS_TABLE: ${self:custom.tables.projects}

@khrome83
Copy link
Author

khrome83 commented Aug 8, 2018

Error when running against root webpack.config.js

 Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
 - configuration.entry should be one of these:
   object { <key>: non-empty string | [non-empty string] } | non-empty string | [non-empty string] | function
   -> The entry point(s) of the compilation.
   Details:
    * configuration.entry should not be empty.
      -> Multiple entry bundles are created. The key is the chunk name. The value can be a string or an array.
    * configuration.entry should be a string.
      -> An entry point without name. The string is resolved to a module which is loaded upon startup.
    * configuration.entry should be an array:
      [non-empty string]
    * configuration.entry should be an instance of function
      -> A Function returning an entry object, an entry string, an entry array or a promise to these things.

Root Webpack Config

const slsw = require("serverless-webpack");
const nodeExternals = require("webpack-node-externals");

module.exports = {
  entry: slsw.lib.entries,
  target: "node",
  // Generate sourcemaps for proper error messages
  devtool: 'source-map',
  // Since 'aws-sdk' is not compatible with webpack,
  // we exclude all node dependencies
  externals: [nodeExternals()],
  mode: slsw.lib.webpack.isLocal ? "development" : "production",
  optimization: {
    // We do not want to minimize our code.
    minimize: false
  },
  performance: {
    // Turn off size warnings for entry points
    hints: false
  },
  // Run babel on all .js files and skip those in node_modules
  module: {
    rules: [
      {
        test: /\.js$/,
        loader: "babel-loader",
        include: __dirname,
        exclude: /node_modules/
      }
    ]
  }
};

@kempsterrrr
Copy link

@khrome83 did you find a resolution to this problem?

I have a small Serverless project which is about to get considerably more complex and as there are only two people working on it I'm learning towards keeping a mono-repo however I'm seeing lots of people mentioning this issues so I'm unsure.

@Enase
Copy link

Enase commented Apr 18, 2019

It's possible to have a single webpack config file in a root, but as a result you must move "serverless-webpack" and "webpack" npm dependencies in a root package.json (and ignore warnings while npm i eg. npm WARN babel-loader@8.0.5 requires a peer of webpack@>=2 but none is installed. You must install peer dependencies yourself.) also you should setup correct webpackConfig path in serverless.yml

@hassankhan
Copy link
Contributor

hassankhan commented Apr 26, 2019

Hi all, just thought I'd share how we manage similar webpack configurations in a monorepo. We use Yarn Workspaces, but you should be able to do without.

We created a @org/webpack-config package that exports a single .js file with the base Webpack config.

Then in each Serverless project inside the monorepo, you can do something like:

services/users/webpack.config.js:

const slsw = require('serverless-webpack');
const baseConfig = require('@org/webpack-config');

module.exports = {
  ...baseConfig,
  entry: slsw.lib.entries,
}

@vamcs
Copy link

vamcs commented Aug 20, 2020

@hassankhan Could you give an example of what the baseConfig would look? This almost worked for me but I'm not sure if it was some wrong Typescript configuration that is preventing me from actually getting somewhere.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants