Git Product home page Git Product logo

alias's People

Contributors

dependabot-preview[bot] avatar dependabot-support avatar dependabot[bot] avatar dhkatz avatar zwingz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

alias's Issues

incorrect path when resolve to the same or the sub folder

this test case will fail:

  ['should support wild card aliases']: {
    options: { config: { paths: { '@/*': ['./src/*'] } } },
    path: './src/pages/Page.ts',
    input: "import module from 'module'\nimport Component from '@/pages/components'",
    output: "import module from 'module'\nimport Component from './components'",
  },

Fix Plugin Export for ES5/ES6 Import

thank you very much for this, i was working on something similar recently myself.

your published version on npm has exports = aliasPlugin instead of module.exports = aliasPlugin
(so it won't work from a regular gulpfile without babel or ts register)

here is a working copy


const path = require("path");
const PluginError = require("plugin-error");
const map = require("map-stream");



function parseImports(file, dir) {
    const results = file.map((line, index) => {
        const imports = findImport(line);
        if (imports === null) {
            return null;
        }
        return {
            path: dir,
            index,
            import: imports,
        };
    });
    return results.filter((value) => {
        return value !== null && value !== undefined;
    });
}

function findImport(line) {
    const matches = line.match(/from (["'])(.*?)\1/);
    if (!matches) {
        return null;
    }
    if (matches.length > 3) {
        throw new PluginError('gulp-ts-alias', 'Multiple imports on the same line are currently not supported!');
    }
    return matches[2];
}

function resolveImports(file, imports, options) {
    const { baseUrl, paths } = options;
    const aliases = {};
    for (const alias in paths) {
        if (!paths.hasOwnProperty(alias)) {
            continue;
        }
        let resolved = alias;
        if (alias.endsWith('/*')) {
            resolved = alias.replace('/*', '/');
        }
        aliases[resolved] = paths[alias];
    }
    const lines = [...file];
    for (const imported of imports) {
        const line = file[imported.index];
        let resolved = '';
        for (const alias in aliases) {
            if (!aliases.hasOwnProperty(alias)) {
                continue;
            }
            if (!imported.import.startsWith(alias)) {
                continue;
            }
            const choices = aliases[alias];
            if (choices !== undefined && choices !== null) {
                resolved = choices[0];
                if (resolved.endsWith('/*')) {
                    resolved = resolved.replace('/*', '/');
                }
                resolved = imported.import.replace(alias, resolved);
            }
            else {
                continue;
            }
            break;
        }
        if (resolved.length < 1) {
            continue;
        }
        let relative = path.relative(path.dirname(imported.path), baseUrl || './');
        relative = path.join(relative, resolved);
        relative = path.relative(path.dirname(imported.path), path.resolve(path.dirname(imported.path), relative));
        relative = relative.replace(/\\/g, '/');
        if (relative.length === 0 || !relative.startsWith('.')) {
            relative = './' + relative;
        }
        lines[imported.index] = line.replace(imported.import, relative);
    }
    return lines;
}
const aliasPlugin = (pluginOptions) => {
    if (pluginOptions.configuration === undefined || pluginOptions.configuration === null) {
        throw new PluginError('gulp-ts-alias', 'The \"configuration\" option cannot be empty. Provide the tsconfig or compilerOptions object.');
    }

    const compilerOptions = pluginOptions.configuration.compilerOptions || pluginOptions.configuration;
    if (compilerOptions.paths === undefined || compilerOptions.paths === null) {
        throw new PluginError('gulp-ts-alias', 'Unable to find the \"paths\" property in the supplied configuration!');
    }
    if (compilerOptions.baseUrl === undefined || compilerOptions.baseUrl === '.') {
        compilerOptions.baseUrl = './';
    }
    return map((file, cb) => {
        if (file.isNull() || !file.contents) {
            return cb(null, file);
        }
        if (file.isStream()) {
            return cb(new PluginError('gulp-ts-alias', 'Streaming is not supported.'));
        }
        const contents = file.contents;
        if (contents === null) {
            return cb(null, file);
        }
        const lines = contents.toString().split('\n');
        const imports = parseImports(lines, file.path);
        if (imports.length === 0) {
            return cb(null, file);
        }
        const resolved = resolveImports(lines, imports, compilerOptions);
        file.contents = Buffer.from(resolved.join('\n'));
        cb(null, file);
    });
};

module.exports = aliasPlugin;

need resolve non-relative imports when provided baseUrl

for example i have ts config:

{"compilerOptions": {
  "baseUrl": "./src"
}}

and have two source files

// pages/MainPage.ts
import {Grid} from "core/Grid";
// core/Grid.ts
export class Grid {}

i need to resolve import path in pages/MainPage.ts
from "core/Grid" to "../core/Grid"
because i have baseUrl in ts config and typescript understand types by path "core/Grid"

Write Tests

I need to write actual tests for this, seeing as people seem to actually have interest in using this package.

error TS5023: Unknown compiler option 'cwd'.

function resolveImports(
  file: ReadonlyArray<string>,
  imports: FileData[],
  options: ts.CompilerOptions
): string[] {
  const { baseUrl, paths, cwd } = options
...
}

cwd is not in type 'ts.CompilerOptions',it cause "error TS5023: Unknown compiler option 'cwd'."

Plugin doesn't recognize paths from extended configs.

Summary

Gulp build is failing with Unable to find the "paths" property in the supplied configuration! error, if configuration doesn't set compilerOptions.paths, but instead extends another configuration using extends property.

Example

├── lib
│   └── // ts-files
├── src
│   └── // ts-files
├── base.tsconfig.json
├── tsconfig.json
└── gulpfile.js
// tsconfig.json
{
  "extends": "./base.tsconfig.json"
}
// base.tsconfig.json
{
  "compilerOptions": {
    "paths": {
      "~/*": ["./lib/*"]
    }
  }
}
// gulpfile.js
const { src, dest } = require("gulp");
const typescript = require('gulp-typescript');
const alias = require('gulp-ts-alias');

const project = typescript.createProject('tsconfig.json');

function build() {
  const compiled = src('./src/**/*.ts')
    .pipe(alias({ configuration: project.config }))
    .pipe(project());

  return compiled.js
    .pipe(dest('build/'))
}

Dynamic Import Support

what about change matches in findImport function to:

const matches = line.match(/from (["'])(.*?)\1/) || line.match(/import\((["'])(.*?)\1\)/);

so, we can do some thing like this:

import("src/test").then(test => test());

Support for Multiple Imports/Line and Auto Aliasing

this will fix double line imports, just make it recursively call for each item in the result array. Needed this for react-hot-loader with sucrase. much fastness.

function findImport(line) {
  const matches = line.match(/from (["'])(.*?)\1/) || line.match(/import\((["'])(.*?)\1\)/) || line.match(/require\((["'])(.*?)\1\)/);

  if (!matches) {
    return null;
  }

  const multiple = [/from (["'])(.*?)\1/g, /import\((["'])(.*?)\1\)/g, /require\((["'])(.*?)\1\)/g].some((exp) => {
    const results = line.match(exp);
    
    console.log(results)
    //
    //return results && results.length > 1
    if(results && results.length > 1){
      results.map(r => findImport(r))
    }
  })

  // if (multiple) {
  //   throw new Error('Multiple imports on the same line are currently not supported!');
  // }

  return matches[2];
}

this config helper looks in src and creates an alias for all the top level folders.. this should handle almost all cases for people with 0 config instead of throwing an error

const fs = require('fs')
const sourceDirs = fs.readdirSync('src').filter(f => (f.indexOf(".") < 1))

let createConfig = () => {
  //todo , merge this with fs read tsconfig + user options + maybe like a web_modules

  const config = {
  baseUrl: ".",
  paths:{}
  }
  for (var x of sourceDirs) {
    config.paths[`${x}/*`] = [`src/${x}/*`]
  }
  //return JSON.stringify(config)
  return config
  }

3
here is my version (for browserify) it works great

var path = require('path')
const {Transform , PassThrough} = require('stream')

/** type{(file: ReadonlyArray<string>, dir: string)}*/
function parseImports(file, dir) {
  const results = file.map((line, index) => {
    const imports = findImport(line);

    if (imports === null) {
      return null;
    }

    return {
      path: dir,
      index,
      import: imports,
    };
  });

  return results.filter((value) => {
    return value !== null && value !== undefined;
  });
}

/** 
 * @type{(line: string)}
 * @returns {string | null}
*/
function findImport(line) {
  const matches = line.match(/from (["'])(.*?)\1/) || line.match(/import\((["'])(.*?)\1\)/) || line.match(/require\((["'])(.*?)\1\)/);

  if (!matches) {
    return null;
  }

  const multiple = [/from (["'])(.*?)\1/g, /import\((["'])(.*?)\1\)/g, /require\((["'])(.*?)\1\)/g].some((exp) => {
    const results = line.match(exp);
    
    console.log(results)
    //
    //return results && results.length > 1
    if(results && results.length > 1){
      results.map(r => findImport(r))
    }
  })

  // if (multiple) {
  //   throw new Error('Multiple imports on the same line are currently not supported!');
  // }

  return matches[2];
}

/** @type {(file: ReadonlyArray<string>, imports: FileData[], options: CompilerOptions) => string[]) }
 * @returns : string[]
*/
function resolveImports(file, imports, options) {
  const { baseUrl, paths } = options;

   

  /** @type { [key: string]: string[] | undefined } */
  const aliases = {};
  for (const alias in paths) {
    /* istanbul ignore else  */
    if (paths.hasOwnProperty(alias)) {
      let resolved = alias;
      if (alias.endsWith('/*')) {
        resolved = alias.replace('/*', '/');
      }

      aliases[resolved] = paths[alias];
    }
  }

  const lines = [...file];
  for (const imported of imports) {
    const line = file[imported.index];

    let resolved = '';
    for (const alias in aliases) {
      /* istanbul ignore else  */
      if (aliases.hasOwnProperty(alias) && imported.import.startsWith(alias)) {
        const choices= aliases[alias];

        if (choices != undefined) {
          resolved = choices[0];
          if (resolved.endsWith('/*')) {
            resolved = resolved.replace('/*', '/');
          }

          resolved = imported.import.replace(alias, resolved);

          break;
        }
      }
    }

    if (resolved.length < 1) {
      continue;
    }

    let relative = path.relative(path.dirname(imported.path), baseUrl || './');
    relative = path.join(relative, resolved);
    relative = path.relative(path.dirname(imported.path), path.resolve(path.dirname(imported.path), relative));
    relative = relative.replace(/\\/g, '/');

    if (relative.length === 0 || !relative.startsWith('.')) {
      relative = './' + relative;
    }

    lines[imported.index] = line.replace(imported.import, relative);
  }

  return lines;
}


const fs = require('fs')
const sourceDirs = fs.readdirSync('src').filter(f => (f.indexOf(".") < 1))

let createConfig = () => {
  //todo , merge this with fs read tsconfig + user options + maybe like a web_modules
  const config = {
  baseUrl: ".",
  paths:{}
  }
  for (var x of sourceDirs) {
    config.paths[`${x}/*`] = [`src/${x}/*`]
  }
  //return JSON.stringify(config)
  return config
  }

const compilerOptions = createConfig()

function compile(filePath, chunk) {
  //const lines = chunk.toString('utf8').split('\n');
  const lines = chunk.split('\n');
  const imports = parseImports(lines, filePath)
  const code = resolveImports(lines, imports, compilerOptions).join('\n')
  return code
}

const unpathifyStream = (file) => {
	if (!/\.tsx?$|\.jsx?$/.test(file) || file.indexOf("node_modules")>0 || file.indexOf("src")<0) {
		return new PassThrough();
	}

	var _transform = new Transform()
	_transform._write = (chunk, encoding, next) => {
		_transform.push(compile(file, chunk.toString('utf8')))
	 	next();
	}
	return _transform
}

module.exports = unpathifyStream

Require Support

At the moment this plugin only supports new import style module imports. This could probably be relatively easily adapted to support require() imports as well.

I personally never use them much, unless I'm requiring a third party module. This makes the need for path resolution in require less useful, because I'd never make any of my own modules need to be used with require().

If anyone finds they need path resolution for this, let me know so I can work on it.

Incorrect relative path calculation

I think this issue is best explained by an example:

Project structure:

dist/
  '-- testing/
src/
  '-- index.ts
tasks/
  '-- build.js
tsconfig.json

tsconfig.json:

{
    // * snip *
    "compilerOptions": {
        "baseUrl": ".",
        "paths": {
            "broken-module": [ "node_modules/@myfork/broken-module" ]
        }
        // * snip *
    }
}

index.ts:

import xxx from 'broken-module'
// * snip *

build.js:

const { src, dest } = require('gulp');
const ts = require('gulp-typescript');
const alias = require('gulp-ts-alias');

const tsProject = ts.createProject('../tsconfig.json');

exports.default = function compile() {
    const tsResult =
        src('../src/**/*.ts')
        .pipe(alias({ configuration: tsProject.config }))
        .pipe(dest('../dist/testing')) // Intermediate output, used to debug generated imports
        .pipe(tsProject());
        
    return tsResult.js.pipe(dest('../dist'));
}

Resulting intermediate dist/testing/index.ts:

import xxx from '../tasks/node_modules/@myfork/broken-module'

// Expected: '@myfork/broken-module' or '../../node_modules/@myfork/broken-module'

My guess would be that it resolves TS's baseUrl against cwd during build time instead of the location of tsconfig.json. Setting the baseUrl to ../ solves the issue (but breaks tsc), further strengthening my suspicions.

Doesn't work with monorepo projects

Everytime the lib tries to get the relative main root so I had to change final configuration to work with.

My tsconfig.json
image

Configuration assign
image

Still tries to resolve path even for commented code

Example

// let ex;

// try {
// 	ex = require("example");
// } catch (err) {
// 	ex = require("example1");
// }

This is fine to cause Multiple imports on the same line are currently not supported! But it should not try to resolve path if code was commented.

it works , when i use alias in my project.But it occurs an error in ts file.

// gulpfile.js

gulp
    .src(entry)
    .pipe(alias({ configuration: tsProject.config }))
    .pipe(tsProject())
    .pipe(gulp.dest('./dist'));
// src/server/controllers/index.ts

import IndexController from '@/controllers/IndexController'; // cannot find module “@/controllers/IndexController” or other type declarations
//  src/server/controllers/IndexController.ts

import Koa from 'koa';

export default class IndexController {
  constructor() {}
  async actionIndex(ctx: Koa.Context, next: Koa.Next) {
    const books = new Books();
    const res = await books.getData();
    ctx.body = res;
  }
}

// tsconfig.json

{
  "compilerOptions": {
    "target": "es5" ,
    "module": "commonjs",
    "paths": {
      "@/controllers": ["./src/server/controllers"],
      "@/models": ["./src/server/models"]
    } ,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  }
}

Although the app run normally, I want to resolve the errors in index.ts

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.