dougmoscrop / serverless-plugin-include-dependencies

MIT License
184 stars 40 forks source link

Not interpreting module aliases #21

Closed jonnyasmar closed 6 years ago

jonnyasmar commented 6 years ago

This is probably more of a feature request than a bug, but I use Serverless with serverless-plugin-typescript and my tsconfig.json contains module aliases like so:

    "paths": {
      "actions/*": ["src/actions/*"],
      "components/*": ["src/components/*"],
      "reducers/*": ["src/reducers/*"],
      "services/*": ["src/services/*"],
      "styles/*": ["src/styles/*"],
      "utils/*": ["src/utils/*"],
      "views/*": ["src/views/*"]
    }

Unfortunately, this plugin does not interpret these aliases, so doing something like:

import { someCoolUtil } from 'utils/someCoolUtil'

results in the following error when deploying:

[serverless-plugin-include-dependencies]: Could not find utils

Any ideas on the level of difficulty to implement such behavior?

dougmoscrop commented 6 years ago

Hmm, I have not used TS myself; how does this work? Like when does "import" read the tsconfig file? I would expect, again not knowing anything about typescript, that this config is read by some kind of ts parser/compiler, so that the output files (.js) all had their paths fixed/adjusted?

jonnyasmar commented 6 years ago

Good question:

You are correct. TypeScript compiles to native JavaScript. So, when it's running the compilation, it references the paths option in tsconfig.json while it parses imported modules.

I've actually started digging into this a little bit, but in limited scope just for my project. Still a WIP right now, but what I have so far is a change to the get-dependency-list.js file in your plugin. My work is around line 52+:

if (name.indexOf('.') === 0) {
        const abs = resolve.sync(name, {
          basedir: path.dirname(currentLocalFile)
        });
        localFilesToProcess.push(abs);
      } else {
        let rootPath = servicePath.split('/');
        rootPath = rootPath.slice(0,rootPath.length-1).join('/');
        try{
          let tsconfig = require(`${rootPath}/tsconfig.json`);
          if(tsconfig.compilerOptions && tsconfig.compilerOptions.paths){
            let paths = tsconfig.compilerOptions.paths;
            let keys = Object.keys(paths);
            keys.forEach(key =>{
              if(mm.isMatch(name,key,{})){
                let matched = mm.capture(key, name);
                console.dir(matched);
                console.dir(`${name} === ${key}`)
              }
            });
          }
        }catch(err){}

        const moduleName = requirePackageName(name.replace(/\\/, '/'));
        const pathToModule = resolvePkg(moduleName, {
          cwd: servicePath
        });

        if (pathToModule) {
          if (utils.isOutside(servicePath, pathToModule)) {
            invalidReference(pathToModule);
          }
          modulePaths.add(pathToModule);
        } else {
          if (ignoreMissing(moduleName)) {
            return;
          }
          throw new Error(`[serverless-plugin-include-dependencies]: Could not find ${moduleName}`);
        }
      }
    });
  }
dougmoscrop commented 6 years ago

Right on - so I'm up for helping support this. I'm not sure I understand why this plugin receives the pre-compiled code though! Is that what you expect?

jonnyasmar commented 6 years ago

Well --- that was a fun exercise at any rate... I got this plugin to interpret the module aliases with the following change:

precinct.paperwork(currentLocalFile).forEach(name => {
      if (resolve.isCore(name) || alwaysIgnored.has(name)) {
        return;
      }

      let rootPath = servicePath.split('/');
      rootPath = rootPath.slice(0,rootPath.length-1).join('/');
      try{
        let tsconfig = require(`${rootPath}/tsconfig.json`);
        if(tsconfig.compilerOptions && tsconfig.compilerOptions.paths){
          let paths = tsconfig.compilerOptions.paths;
          let keys = Object.keys(paths);
          keys.forEach(key =>{
            if(mm.isMatch(name,key,{})){
              let matched = mm.capture(key, name)[0];
              name = `${servicePath}/${paths[key][0].replace('*',matched)}`;
              name = path.relative(path.dirname(currentLocalFile),name);
            }
          });
        }
      }catch(err){}

      if (name.indexOf('.') === 0) {
        const abs = resolve.sync(name, {
          basedir: path.dirname(currentLocalFile)
        });
        localFilesToProcess.push(abs);
      } else {
        const moduleName = requirePackageName(name.replace(/\\/, '/'));
        const pathToModule = resolvePkg(moduleName, {
          cwd: servicePath
        });

        if (pathToModule) {
          if (utils.isOutside(servicePath, pathToModule)) {
            invalidReference(pathToModule);
          }
          modulePaths.add(pathToModule);
        } else {
          if (ignoreMissing(moduleName)) {
            return;
          }
          throw new Error(`[serverless-plugin-include-dependencies]: Could not find ${moduleName}`);
        }
      }
    });

Only to ultimately realize that the issue is actually with serverless-plugin-typescript itself... It apparently does not process the tsconfig.json at all. See https://github.com/graphcool/serverless-plugin-typescript/issues/27

I knew I thought something was fishy when I noticed your plugin was acting against the .build directory. This should be handled by serverless-plugin-typescript, not here. Sorry to waste your time :(

dougmoscrop commented 6 years ago

I think I can close this based on your last comment. Please re-open if there's anything that can or should be done on this side!