Coder Social home page Coder Social logo

bundler's Introduction

bundler's People

Contributors

dependabot-preview[bot] avatar dependabot-support avatar nuintun avatar

Stargazers

 avatar

Watchers

 avatar

bundler's Issues

路径 resolve 异步实现

/**
 * @module index
 */

type dependencies = string[];
type FileList = FastMap<File>;
type DependencyGraph = FastMap<Dependency>;
type setMarkedNode = (path: string, referer: string | null) => string;
type drawGraphNode = (src: string, referer: string | null) => Promise<void>;
type updateGraphNode = (referer: string | null, src: string, path: string) => void;
type drawDependencyGraph = (src: string, referer: string | null) => Promise<[DependencyGraph, FileList]>;

export type resolve = (src: string, referer: string) => string | Promise<string>;
export type parse = (path: string) => void | ParseResult | Promise<void | ParseResult>;

export interface Options {
  parse: parse;
  cycle?: boolean;
  resolve: resolve;
  [key: string]: any;
}

export interface ParseResult {
  readonly contents?: any;
  readonly dependencies?: dependencies;
}

export interface File {
  readonly path: string;
  readonly contents: any;
  readonly dependencies: dependencies;
}

interface MarkedNode {
  readonly referer: string | null;
  readonly dependencies: IterableIterator<string>;
}

const { hasOwnProperty }: Object = Object.prototype;

class FastMap<T> {
  private map: { [key: string]: T } = Object.create(null);

  set(key: string, value: T): void {
    this.map[key] = value;
  }

  get(key: string): T {
    return this.map[key];
  }

  has(key: string): boolean {
    return hasOwnProperty.call(this.map, key);
  }
}

class Dependency {
  private dependencies: dependencies;
  private indexes: { [src: string]: number };

  set(dependencies: dependencies): void {
    this.dependencies = [];
    this.indexes = Object.create(null);

    const { length }: dependencies = dependencies;

    for (let index: number = 0; index < length; index++) {
      const src: string = dependencies[index];

      if (!hasOwnProperty.call(this.indexes, src)) {
        this.indexes[src] = index;
      }
    }
  }

  update(src: string, path: string): void {
    this.dependencies[this.indexes[src]] = path;
  }

  values(): IterableIterator<string> {
    return new Set<string>(this.dependencies).values();
  }
}

async function readFile(path: string, parse: parse): Promise<File> {
  const { contents = null, dependencies }: ParseResult = (await parse(path)) || {};

  return { path, contents, dependencies: Array.isArray(dependencies) ? dependencies : [] };
}

function drawDependencyGraph(input: string, options: Options): Promise<[DependencyGraph, FileList]> {
  return new Promise<[DependencyGraph, FileList]>((resolve, reject) => {
    let remaining: number = 0;
    let hasError: boolean = false;

    const files: FileList = new FastMap();
    const graph: DependencyGraph = new FastMap();

    const updateGraphNode: updateGraphNode = (referer, src, path) => {
      referer !== null && graph.get(referer).update(src, path);
    };

    const drawGraphNode: drawGraphNode = async (src, referer) => {
      if (!hasError) {
        remaining++;

        try {
          const path: string = referer !== null ? await options.resolve(src, referer) : src;

          if (!graph.has(path)) {
            updateGraphNode(referer, src, path);

            const dependency: Dependency = new Dependency();

            graph.set(path, dependency);

            const file: File = await readFile(path, options.parse);

            files.set(path, file);

            dependency.set(file.dependencies);

            for (const src of file.dependencies) {
              drawGraphNode(src, path);
            }
          } else {
            updateGraphNode(referer, src, path);
          }
        } catch (error) {
          hasError = true;

          return reject(error);
        }

        if (!hasError && !--remaining) {
          return resolve([graph, files]);
        }
      }
    };

    drawGraphNode(input, null);
  });
}

function assert(options: Options): never | Options {
  // Assert resolve and parse
  ['resolve', 'parse'].forEach((option: string) => {
    if (options && typeof options[option] !== 'function') {
      throw new TypeError(`The options.${option} must be a function`);
    }
  });

  return options;
}

export default class Bundler {
  private options: Options;

  constructor(options: Options) {
    this.options = assert(options);
  }

  /**
   * @public
   * @method parse
   * @param {string} input
   * @returns {Promise<File[]>}
   * @description Get the list of dependent files of input file
   */
  async parse(input: string): Promise<File[]> {
    const output: File[] = [];
    const { options }: Bundler = this;
    const waiting: Set<string> = new Set();
    const marked: FastMap<MarkedNode> = new FastMap();
    const [graph, files]: [DependencyGraph, FileList] = await drawDependencyGraph(input, options);

    const setMarkedNode: setMarkedNode = (path, referer) => {
      waiting.add(path);

      marked.set(path, { referer, dependencies: graph.get(path).values() });

      return path;
    };

    let current: string | null = setMarkedNode(input, null);

    while (current !== null) {
      const node: MarkedNode = marked.get(current);
      const { done, value: path }: IteratorResult<string> = node.dependencies.next();

      if (done) {
        waiting.delete(current);

        output.push(files.get(current));

        current = marked.get(current).referer;
      } else {
        if (waiting.has(path)) {
          // Allow circularly dependency
          if (options.cycle) continue;

          // When not allowed cycle throw error
          throw new ReferenceError(`Found circularly dependency ${path} at ${current}`);
        }

        if (!marked.has(path)) {
          current = setMarkedNode(path, current);
        }
      }
    }

    return output;
  }
}

路径 resolve 异步会拉低性能和最大文件深度!!

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.