Typescript prod deployment

Ok, so…
I’m trying to deploy and everything worked like a charm until the step:

2:59:09 PM: │ 2. onPostBuild command from @netlify/plugin-functions-core │

I’m using TS, partly. I had to move a couple of things from ./services to ./lib to get things to work and it has done the trick for a couple of times but now I’m stuck on the importAll call in functions/graphql.js - doesn’t look like it’s working on Netlify, however dev builds have no issue with it.

If anyone wondered how we could use Typescript today with RW, that’s my updated report, on v0.10.0 :). I’d be glad to expand my knowledge while trying to figure out how to contribute on TS support here but I’m a tad clueless about where to start ( meaning, how to debug a production build #jsnoob ).

Plus, if anyone has some meaningful insight, I’m a buyer

Thomas

Thanks for the update!

I had to move a couple of things from ./services to ./lib to get things to work

^^ hmm, that’s definitely going to break some things. Could you be more specific?

however dev builds have no issue

^^ do you mean when you run yarn rw dev or when you run yarn rw build? The latter is what runs when you build for deploy (check out netlify.toml if you haven’t before.

Did you create a tsconfig.json, and, if so, could you share the code? Also, do you have one tsconfig in root or do you have any in api/ or web/?

1 Like

From ./services to ./lib

I had a utils.ts at the root of ./services, the build didn’t like it so I moved it to ./lib, which, in retrospect, might be a more appropriate place. My usual services didn’t pose the same issue as this utils.ts, so maybe it’s nothing to fear from :).

tsconfig.json

I have some! Not at the root however :), one in ./api, one in ./web. Configuration on node projects is a blackbox topic to me, I’ve always avoided digging into it, so take it as my naïve approach to try and get things to work: I might have c/c a couple of lines and haven’t kept track of them.

./api/tsconfig.json
{
  "compilerOptions": {
    "target": "esnext",
    "moduleResolution": "node",
    "module": "esnext",
    "strict": true,
    "esModuleInterop": true,
    "baseUrl": "./",
    "paths": {
      "src/*": ["./src/*"]
    },
    "declaration": true,
    "declarationDir": "./dist"
  },
  "include": ["src"]
}
./web/tsconfig.json
{
  "compilerOptions": {
    "jsx": "react",
    "baseUrl": ".",
    "paths": {
      "src/*": [
        "./src/*"
      ]
    },
    "allowSyntheticDefaultImports": true,
    "lib": [
      "es6",
      "dom"
    ],
    "noImplicitAny": false,
    "noImplicitThis": true,
    "strictNullChecks": true
  },
  "include": [
    "src/**/*"
  ]
}

Last but not least: I meant yarn rw dev yields no troubles. I’ll try with yarn rw build locally.

1 Like

So, I executed the build locally, it did not go well.
All my js files can be found in the dist directory, but the ts files and their parents directories, if they do not contain js files, are completely ignored.

That’s what kills my prod build.

Hey Noire!

Can you share some of your code?

We can attempt to isolate the problem by specifying each side:
yarn rw build api
yarn rw build web

I’ve converted my “api” side to typescript over here: https://github.com/redwoodjs/example-invoice/tree/master/api

Hi Peter \o/

My code is on a private repo, I can add you but we’ll have to process via MP :slight_smile: ( and, it’s on bitbucket ).

Below you will find the scratching example of a .ts that gets ignored on build ( sorry if your eyes bleed, as you may recall, I’m brand new to the js-gig :slight_smile: ).
Also, I’ve had a look at your file ( which allows me to understand how I incorrectly summoned the currentUser :bowing_man: ), and comparatively I’m not sure I’d understand what would be so messed up in the following example that would justify it not being included in the build.
I’ll build your project locally to have a look though :slight_smile:

import { context } from '@redwoodjs/api'
import { db } from 'src/lib/db'
import { InterfacePayload, Status } from 'src/lib/utils'

import { getDefaultCollection } from 'src/services/collections/collections.js'

type OwnershipProps = {
  id: string
}

const ownership: (props: OwnershipProps) => Promise<object> = function ({
  id,
}) {
  return db.ownership.findOne({
    where: { id },
    include: {
      book: {
        select: {
          title: true,
          covers: true,
        },
      },
      collection: {
        select: {
          id: true,
          name: true,
        },
      },
    },
  })
}

const ownerships: () => Promise<object> = () =>
  db.user
    .findOne({
      where: { id: context?.currentUser?.id },
      include: {
        ownerships: {
          include: {
            book: {
              include: { authors: true },
            },
            collection: { select: { id: true, name: true } },
          },
        },
      },
    })
    .then(
      (result: object) => result?.ownerships,
      (reason: string) => reason
    )

type ClaimBookOwnershipProps = {
  bookId: string
  rate: number
}

async function claimBookOwnership({
  bookId,
  rate,
}: ClaimBookOwnershipProps): Promise<InterfacePayload> {
  const defaultCollection = await getDefaultCollection({
    id: context.currentUser.id,
  })

  return db.ownership
    .upsert({
      where: {
        book_user: { bookId, collectionId: defaultCollection.id },
      },
      create: {
        rate,
        collection: {
          connect: {
            id_name: { id: defaultCollection.id, name: defaultCollection.name },
          },
        },
        book: { connect: { id: bookId } },
        User: { connect: { id: context?.currentUser?.id } },
      },
      update: {
        rate,
      },
      select: {
        book: {
          select: { title: true },
        },
      },
    })
    .then(
      ({ book }) => {
        const claimBookOwnershipSuccess = `'${book?.title}' successfully added`
        return {
          data: book,
          message: claimBookOwnershipSuccess,
          status: Status.success,
        }
      },
      (reason) => {
        const claimBookOwnershipFailure = `Book couldn't be added ( ${reason} ).`
        return {
          message: claimBookOwnershipFailure,
          status: Status.failure,
        }
      }
    )
}

type UpdateOwnershipProps = {
  id: string
  data: { rate: number; note: string }
}

async function updateOwnership({
  id,
  input,
}: UpdateOwnershipProps): Promise<InterfacePayload> {
  return db.ownership
    .update({
      data: { ...input, updatedAt: new Date() },
      where: { id },
      include: {
        book: {
          select: {
            title: true,
            covers: true,
          },
        },
      },
    })
    .then(
      (ownership) => ({
        status: Status.success,
        message: `Book '${ownership.book.title}' successfully updated!`,
        data: ownership,
      }),
      (reason) => ({ status: Status.failure, message: reason })
    )
}

export { ownership, ownerships, claimBookOwnership, updateOwnership }
1 Like

WTF alert: I ran yarn rw build api and even though the project cloned and built successfully and I have invoice.ts, it did not work.
I’m pasting my info:

  System:
    OS: Linux 5.4 Ubuntu 20.04 LTS (Focal Fossa)
    Shell: 5.8 - /usr/bin/zsh
  Binaries:
    Node: 13.14.0 - /tmp/yarn--1592220626332-0.3129817143010425/node
    Yarn: 1.22.4 - /tmp/yarn--1592220626332-0.3129817143010425/yarn
  Browsers:
    Chrome: 83.0.4103.97
  npmPackages:
    @redwoodjs/core: ^0.8.2-canary.2 => 0.8.2-canary.2+3b03bf0 

Does it actually work for you?

1 Like

Ah, I see what you mean, I’ll dig into the build command, we probably need to add the supported extensions to it. :man_facepalming:

You might want to try upgrading soon, especially if you’re using Auth package as Peter’s been continually making improvements. However, I don’t believe this will fix the build issue, unfortunately. :confused:

1 Like

I’ve fixed this over here: https://github.com/redwoodjs/redwood/pull/703

1 Like

Hello guys!
Many thanks for the messages :slightly_smiling_face:
I missed it, I saw the extensions specified in a build command I mistook for the one @peterp fixed.
I’m eager to test it, unfortunately my laptop is sitting on the doctor’s bench for a couple of days. I’ll try it out as soon as I get it back :slightly_smiling_face: and keep you updated!
Thanks again \o/

1 Like

@peterp I’ve ran the command both locally and on netlify, the issue doesn’t seem to be sorted.
Here’s my tree for services:

api/src/services
├── authors
│   └── authors.js
├── books
│   ├── books.ts
│   └── strategy
│       ├── exceptions.js
│       ├── google.js
│       └── nodeisbn.ts
├── categories
│   └── categories.js
├── collections
│   └── collections.js
├── labels
│   ├── labels.test.js
│   └── labels.ts
├── localisations
│   └── localisations.js
├── ownerships
│   └── ownerships.ts
├── publishers
│   └── publishers.js
├── tokens
│   └── tokens.js
└── users
    └── users.ts

The .js is handled properly but the .ts is still completely ignored, locally and on netlify.
I have pulled the last version of redwoodjs including your branch that fixes the deploy of .ts files and confirmed that it was the proper package.json that was used with the updated command.

I have also tried it on the invoice-example and can confirm it does not work either. Have you had success with it?

Seeing the same issue as @noire.munich - tried patch-package with your changes, but no luck. This is the error log for reference:

2:41:49 PM:   Error message
2:41:49 PM:   Error: In file "/opt/build/repo/api/dist/functions/graphql.js": Cannot find module '../lib/auth' from '/opt/build/repo/api/dist/functions'
2:41:49 PM: ​
2:41:49 PM:   Plugin details
2:41:49 PM:   Package:        @netlify/plugin-functions-core
2:41:49 PM:   Version:        2.0.10
2:41:49 PM:   Repository:     git+https://github.com/netlify/build.git
2:41:49 PM:   npm link:       https://www.npmjs.com/package/@netlify/build
2:41:49 PM:   Report issues:  https://github.com/netlify/build/issues
2:41:49 PM: ​
2:41:49 PM:   Error location
2:41:49 PM:   In "onPostBuild" event in "@netlify/plugin-functions-core" from core
2:41:49 PM:       at /opt/buildhome/.netlify-build-nvm/versions/node/v12.16.3/lib/node_modules/@netlify/build/node_modules/resolve/lib/async.js:142:35
2:41:49 PM:       at load (/opt/buildhome/.netlify-build-nvm/versions/node/v12.16.3/lib/node_modules/@netlify/build/node_modules/resolve/lib/async.js:161:43)
2:41:49 PM:       at onex (/opt/buildhome/.netlify-build-nvm/versions/node/v12.16.3/lib/node_modules/@netlify/build/node_modules/resolve/lib/async.js:186:17)
2:41:49 PM:       at /opt/buildhome/.netlify-build-nvm/versions/node/v12.16.3/lib/node_modules/@netlify/build/node_modules/resolve/lib/async.js:13:69
2:41:49 PM:       at FSReqCallback.oncomplete (fs.js:167:21)
2:41:49 PM: ​
2:41:49 PM:   Error properties
2:41:49 PM:   { code: 'MODULE_NOT_FOUND' }

It looks like a post-build issue, when netlify is trying to deploy functions.

Update: just verified its not producing the files locally either

Hi all, just checking in here – any updates on progress or fixes here?

Thanks for being TS trailblazers! But we understand the pain of trailblazing can be no fun.

1 Like

I did the thing where I thought I fixed it, but didn’t test it. I’ve now actually fixed it and tested it in this PR: https://github.com/redwoodjs/redwood/pull/744

Hi @thedavid and @peterp - thanks for checking in and the fix.

Unfortunately this looks like a partial fix, it now generates the js files but the imports in the generated graphql.js looks incorrect. The build still fails trying to import services in ts. In my case its github.ts

Netlify build log snippet

12:33:21 PM:   Error message
12:33:21 PM:   Error: In file "/opt/build/repo/api/dist/functions/graphql.js": Cannot find module './../services/github/github.ts' from '/opt/build/repo/api/dist/functions'

Snippet from generated graphql.js

.
.
var _servicesGithubGithubTs = _interopRequireWildcard(require("./../services/github/github.ts"));

var _servicesGithubTypesTs = _interopRequireWildcard(require("./../services/github/types.ts"));
.
.

Its probably the importAll.macro that doesn’t know how to handle ts. I tried modifying it a bit - but it looks like its depracated anyway. Maybe I should import services differently? An example would be really helpful (without needing to import each service by hand ideally!)

Indeed! **shakes fist at importAll.macro**

Could you try: import services from 'src/services/*.{js,ts}' instead?

At the moment, we do not support nested directories in services.

Yep tried that, but services end up an empty array (in the compiled code)!

Ok, I’m going to have to modify this to support sub-directories in the services folder.

Before you do, hold on I think the reason its failing is because it tries to import the tests also. Removed all the service tests (!!!) and it seems to work in dev. Waiting for netlify build now :slight_smile:

I’m using this to get through subdirectories:

import services from 'src/services/*/*.{js,ts}'
1 Like