npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@mega-apps/vue-addon-loader

v1.0.0-beta.9

Published

Vue addon loader, for SFCs and components. You can use it for vue component online.

Downloads

4

Readme

@mega-apps/vue-addon-loader

Vue 扩展加载器,适用于动态加载 Vue 组件。

Vue Addon Loader is a small library that allows you to load Vue components from a module dynamically. It's only dependent on the vue runtime. no need to install any other dependencies. No node.js or webpack dependencies.

1. 主要特性

  • 完全支持 Vue2 的组件
  • 可支持IE11版本
  • 仅需要Vue运行时,无需其他依赖
  • 提供 esm and umd
  • 支持 JSX 语法
  • 支持 嵌入ES6模块写法
  • 支持自定义 CSS、HTML、脚本支持
  • 支持 SFC 自定义模块
  • 支持编译错误定位
  • 支持远程组件,来源于网络、数据库、本地文件... 等

安装


# yarn 安装
yarn add @mega-apps/vue-addon-loader

# 或 pnpm 安装
pnpm add @mega-apps/vue-addon-loader

简单示例

2. 支持 JSX 语法

<script>
  export default {
    data() {
      return {
        type: 'jsx 代码',
        msg: 'Hello Vue!'
      }
    },
    render(h) {
      return (
        <div class="flex flex-col items-center justify-center border border-gray-600 p-4 m-2">
          <h1 class="text-xl font-bold"> 语法特性 【{this.type}】</h1>
          {/* 注释 */ this.msg }
        </div>
      )
    }
  }
</script>

3. 支持的ECMAScript 特性

3.1. 支持计算属性: computed-properties

const foo = 'foo', bar = 'bar';
var obj = {
  ["x" + foo]: "heh",
  ["y" + bar]: "noo",
  foo: "foo",
  bar: "bar",
};

3.2. 支持属性名表达式: property-name-expression

const foo = 'foo', bar = 'bar';
var obj = {
  [foo]: "heh",
  [bar]: "noo",
  foo: "foo",
  bar: "bar",
};

3.3. 支持属性简写: property-shorthand

const foo = 'foo', bar = 'bar';
var obj = {
  foo,
  bar,
  foo: "foo",
  bar: "bar",
};

3.4. 支持属性绑定: property-binding

const foo = 'foo', bar = 'bar';
var obj = {
  foo: "heh",
  bar: "noo",
  foo: "foo",
  bar: "bar",
};

3.5. 支持BigInt 大整数

const expected = 4n / 2n;

3.6. 支持模板字符串: template-string

const foo = 'foo', bar = 'bar';
const info = `${foo} ${bar}`;

3.7. 支持箭头函数: arrow-function

const foo = 'foo', bar = 'bar';
const info = (foo, bar) => {
  return foo + bar;
};

3.8. 支持类: class

class Foo {
  constructor(foo, bar) {
    this.foo = foo;
    this.bar = bar;
  }
}

3.9. 支持类继承: class-extends

class Foo extends Bar {
  constructor(foo, bar) {
    super(foo, bar);
    this.foo = foo;
    this.bar = bar;
  }
}

3.10. 支持类静态属性: class-static-properties

class Foo {
  static foo = 'foo';
  static bar = 'bar';
}

3.11. 支持类静态方法: class-static-methods

class Foo {
  static foo() {
    return 'foo';
  }
  static bar() {
    return 'bar';
  }
}

3.12. 支持类实例属性: class-instance-properties

class Foo {
  foo = 'foo';
  bar = 'bar';
}

3.13. 支持类实例方法: class-instance-methods

class Foo {
  foo() {
    return 'foo';
  }
  bar() {
    return 'bar';
  }
}

3.14. 支持类静态属性和方法: class-static-properties-and-methods

class Foo {
  static foo = 'foo';
  static bar() {
    return 'bar';
  }
}

3.15. 支持类实例属性和方法: class-instance-properties-and-methods

class Foo {
  foo = 'foo';
  bar() {
    return 'bar';
  }
}

3.16. 支持类静态属性和实例属性: class-static-properties-and-instance-properties

class Foo {
  static foo = 'foo';
  foo = 'foo';
}

3.17. 支持类静态属性和实例方法: class-static-properties-and-instance-methods

class Foo {
  static foo = 'foo';
  foo() {
    return 'foo';
  }
}

3.18. 支持异步生成器: async-generator

async function* foo() {
  yield 1;
  yield 2;
  yield 3;
}

3.19. 支持类型断言: type-assertion

const foo = 'foo';
const bar = 'bar';
const info = foo as string;

3.20. 支持类型保护: type-guard

if (foo instanceof Foo) {
  foo.foo();
}

3.21. 支持类型别名: type-alias

type Foo = {
  foo: string;
  bar: string;
};

3.22. 支持类私有属性和方法: private-property-and-methods

class Foo {
  #foo = 'foo';
  #bar() {
    return 'bar';
  }
}

3.23. 支持类属性修饰符: property-modifiers

class Foo {
  #foo = 'foo';
  #bar() {
    return 'bar';
  }
  get foo() {
    return this.#foo;
  }
  set foo(value) {
    this.#foo = value;
  }
  get bar() {
    return this.#bar();
  }
}

3.24. 支持类静态块: class-static-block

class Foo {
  static foo = 'foo';
  static bar = 'bar';
  static #baz = 'baz';
  static #qux() {
    return 'qux';
  }
}

3.25. 支持类实例块: class-instance-block

class Foo {
  foo = 'foo';
  bar = 'bar';
  #baz = 'baz';
  #qux() {
    return 'qux';
  }
}

3.26. 支持动态导入: dynamic-import

import('./foo.js').then(foo => {
  foo.foo();
});

3.27. 支持模块化导入: module

import { foo } from './foo.js';

3.28. 支持模块化默认导入: module-with-default-export

import foo from './foo.js';

3.29. 支持导出命名空间: export-namespace

export * as foo from './foo.js';

3.30. 支持导出类: export-class

export class Foo {
  constructor(foo, bar) {
    this.foo = foo;
    this.bar = bar;
  }
}

3.31. 支持函数sent特性: function-sent

function foo(x = (y = z)) {
  return x;
}

function *adder(total=0) {
  let increment=1;
  do {
      switch (request = function.sent){
          case undefined: break;
          case "done": return total;
          default: increment = Number(request);
      }
      yield total += increment;
  } while (true)
}

3.32. 支持逻辑赋值: logical-assignment-operators

let b = 0;
let a = b ||= 1;

3.33. 支持模块字符串: module-strings

import { "😄" as smile } from "emojis";

3.34. 支持空选链: nullish-coalescing-operator

let foo = bar ?? 'foo';

3.35. 支持空赋值: nullish-assignment-operator

let foo = bar ??= 'foo';

3.36. 支持对象rest spread: object-rest-spread

let { foo, ...bar } = { foo: 'foo', bar: 'bar' };

3.37. 支持数组rest spread: array-rest-spread

let [foo, ...bar] = ['foo', 'bar'];

3.38. 支持可选捕获绑定: optional-catch-binding

try{
  console.log(1);
}finally{
  console.log('finally');
}

3.39. 支持可选参数: optional-parameters

function foo(x = 1) {
  return x;
}

3.40. 支持可选参数和可选属性: optional-chaining

const foo = {
  bar: {
    baz: 'baz'
  }
};

const baz = foo?.bar?.baz;

3.41. 支持私有In: private-in

class C {
  #brand;

  #method() {}

  get #getter() {}

  static isC(obj) {
    return #brand in obj && #method in obj && #getter in obj;
  }
}

3.42. 支持顶级 await: top-level-await

async function foo() {
  await bar();
}

// 依赖回滚
let jQuery;
try {
  jQuery = await import('https://cdn-a.com/jQuery');
} catch {
  jQuery = await import('https://cdn-b.com/jQuery');
}

4. 支持ECMAScript 提案

4.1. 支持异步do表达式: async-do-expressions

async
do {
  await fetch('http://www.bing.com').json()
}

4.2. 支持decimal

let budget = 1_000_000_000_000;
console.log(budget === 10 ** 12); // true

let nibbles = 0b1010_0001_1000_0101;
console.log(!!(nibbles & (1 << 7))); // true

// Messages are sent as 24 bit values, but should be
// treated as 3 distinct bytes:
let message = 0xa0_b0_c0;

// What's the value of the upper most byte? It's A0, or 160.
// We can confirm that:
let a = (message >> 16) & 0xff;
console.log(a.toString(16), a); // a0, 160

// What's the value of the middle byte? It's B0, or 176.
// Let's just make sure...
let b = (message >> 8) & 0xff;
console.log(b.toString(16), b); // b0, 176

// What's the value of the lower most byte? It's C0, or 192.
// Again, let's prove that:
let c = message & 0xff;
console.log(c.toString(16), b); // c0, 192

4.3. 支持装饰器:decorators

function logged(value, { kind, name }) {
  if (kind === "method") {
    return function (...args) {
      console.log(\`starting $\{ name \} with arguments $\{ args.join(", ") \} \`);
      const ret = value.call(this, ...args);
      console.log(\`ending $\{ name \}\`);
      return ret;
    };
  }
}

class C {
  @logged
  m(arg) {}
}

4.4. 支持do表达式: do-expressions

let x = do {
  let y = 1;
  y + 1;
};

4.5. 支持导出默认值: export-default-from

export default from './foo.js';

4.6. 支持导出from: export-from

export v from 'vue';
export default {
  data() {
    return {
      type: 'exportDefaultFrom export from 语法',
      msg: 'https://github.com/tc39/ecmascript-export-default-from',
      isProposal: true
    }
  },
  ${commonCode}
}

4.7. 支持函数绑定: function-bind

            const box = {
              weight: 2,
              getWeight() {
                return this.weight;
              },
            };

            const { getWeight } = box;

            console.log(box.getWeight()); // prints '2'

            const bigBox = { weight: 10 };
            console.log(bigBox::getWeight()); // prints '10'

            // Can be chained:
            function add(val) {
              return this + val;
            }

            console.log(bigBox::getWeight()::add(5)); // prints '15'

4.8. 支持导入断言: import-assert

import Vue from "vue" assert { type: "js" };

4.9. 支持局部模块化: module-declaration

 let m = module { export let y = 1; };

4.10. 支持 局部应用: partial-application

function add(x, y) { return x + y; }

const addOne = add(1, ?); // apply from the left
addOne(2); // 3

const addTen = add(?, 10); // apply from the right
addTen(2); // 12

4.11. 支持流水线操作符: pipeline-operator

let result = "hello"
              |> console.log;

4.12. 支持 Record 和 元组类型

let x = #{x: 1, y: 2};
let y = #[1, 2];

4.13. 支持抛出异常表达式: throw-expressions

class Product {
  get id() { return this._id; }
  set id(value) { this._id = value || throw new Error("Invalid value"); }
}

5. 代码示例

// TODO: 要支持TailwindCss 的at rule

const commonCode = `
  render(h) {
    return (
      <div class="flex flex-col items-center justify-center border border-gray-600 p-4 m-2">
        <h1 class="text-xl font-bold"> { !!this?.isProposal ? 'ECMAScript 提案' : 'ECMAScript 特性' } 【{this.type}】</h1>

        <a href={/* 注释 */ this.msg } class="bg-blue-500"> { this.msg } </a>
      </div>
    )
  }
`

export default {
  base: {
    "demo": `
      <template>
        <span class="bg-yellow-200 p-4">Hello from Vue {{ require('myCustomModel').vueVersion }} !</span>
      </template>
    `,
  },
  // 验证是否支持tailwindcss的at rule
  // 试验场:https://play.tailwindcss.com/
  // TODO:嵌入tailwind 的编译器,用来解析at rule,参照 https://play.tailwindcss.com/ 网站源码
  tailwindcss: {
    "at-rules": {
      getContentData: () => {
        return `
          <template>
            <div class="flex flex-col items-center justify-center border border-gray-600 p-4 m-2">
              <h1 class="text-xl font-bold">验证TailwindCss at rule 是否可用</h1>
              <button class="w-32 rounded-md bg-gray-300 customBtn">Button</button>
            </div>
          </template>
          <style scoped>
            .customBtn {
              // 指令不可用
              @apply bg-blue-700 text-white;
            }
          </style>
        `
      },
      type: '.vue'
    }
  },
  // 语法特性
  syntax: {
    "jsx": {
      getContentData: () => {
        return `
          <script>
            export default {
              data() {
                return {
                  type: 'jsx 代码',
                  msg: 'Hello Vue!'
                }
              },
              render(h) {
                return (
                  <div class="flex flex-col items-center justify-center border border-gray-600 p-4 m-2">
                    <h1 class="text-xl font-bold"> 语法特性 【{this.type}】</h1>
                    {/* 注释 */ this.msg }
                  </div>
                )
              }
            }
          </script>
        `
      },
      type: '.vue'
    },
    "__filename": {
      getContentData: () => {
        return `
          <script>
            export default {
              data() {
                return {
                  type: '__filename 注入',
                  msg: __filename
                }
              },
              render(h) {
                return (
                  <div class="flex flex-col items-center justify-center border border-gray-600 p-4 m-2">
                    <h1 class="text-xl font-bold"> 语法特性 【{this.type}】</h1>
                    {/* 注释 */ this.msg }
                  </div>
                )
              }
            }
          </script>
        `
      },
      type: '.vue'
    },
    "__dirname": {
      getContentData: () => {
        return `
          <script>
            export default {
              data() {
                return {
                  type: '__dirname 注入',
                  msg: __dirname
                }
              },
              render(h) {
                return (
                  <div class="flex flex-col items-center justify-center border border-gray-600 p-4 m-2">
                    <h1 class="text-xl font-bold"> 语法特性 【{this.type}】</h1>
                    {/* 注释 */ this.msg }
                  </div>
                )
              }
            }
          </script>
        `
      },
      type: '.vue'
    },
    //#region ECMASript 特性
    "computed-properties": {
      getContentData: () => {
        return `
          <script>
            const foo = 'foo', bar = 'bar';
            var obj = {
              ["x" + foo]: "heh",
              ["y" + bar]: "noo",
              foo: "foo",
              bar: "bar",
            };

            export default {
              data() {
                return {
                  type: 'computed-properties 计算属性',
                  msg: 'https://babeljs.io/docs/en/babel-plugin-transform-computed-properties'
                }
              },
              render(h) {
                return (
                  <div class="flex flex-col items-center justify-center border border-gray-600 p-4 m-2">
                    <h1 class="text-xl font-bold"> 语法特性 【{this.type}】</h1>
                   
                    <a href={/* 注释 */ this.msg } class="bg-blue-500"> { this.msg } </a>
                  </div>
                )
              }
            }
          </script>
        `
      },
      type: '.vue'
    },
    "BigInt": {
      getContentData: () => {
        return `
          <script>
            const expected = 4n / 2n;

            export default {
              data() {
                return {
                  type: 'BigInt 大整数',
                  msg: 'https://github.com/tc39/proposal-bigint'
                }
              },
              render(h) {
                return (
                  <div class="flex flex-col items-center justify-center border border-gray-600 p-4 m-2">
                    <h1 class="text-xl font-bold"> 语法特性 【{this.type}】</h1>
                   
                    <a href={/* 注释 */ this.msg } class="bg-blue-500"> { this.msg } </a>
                  </div>
                )
              }
            }
          </script>
        `
      },
      type: '.vue'
    },
    "asyncGenerators": {
      getContentData: () => {
        return `
          <script>

            const fn1 = async function* () {};

            export default {
              data() {
                return {
                  type: 'asyncGenerators 异步迭代器,生成器',
                  msg: 'https://github.com/tc39/proposal-async-iteration'
                }
              },
              render(h) {
                return (
                  <div class="flex flex-col items-center justify-center border border-gray-600 p-4 m-2">
                    <h1 class="text-xl font-bold"> 语法特性 【{this.type}】</h1>
                   
                    <a href={/* 注释 */ this.msg } class="bg-blue-500"> { this.msg } </a>
                  </div>
                )
              }
            }
          </script>
        `
      },
      type: '.vue'
    },
    "classProperties": {
      getContentData: () => {
        return `
          <script>

            class A { b = 1; }

            export default {
              data() {
                return {
                  type: 'classProperties 类字段',
                  msg: 'https://github.com/tc39/proposal-class-public-fields'
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "classPrivateProperties": {
      getContentData: () => {
        return `
          <script>

            class A { #b = 1; }

            export default {
              data() {
                return {
                  type: 'classPrivateProperties 类私有字段',
                  msg: 'https://github.com/tc39/proposal-private-fields'
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "classPrivateMethods": {
      getContentData: () => {
        return `
          <script>

            class A { #c() {} }

            export default {
              data() {
                return {
                  type: 'classPrivateMethods 类私有方法',
                  msg: 'https://github.com/tc39/proposal-private-methods'
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "classStaticBlock": {
      getContentData: () => {
        return `
          <script>

            class A { static {} }

            export default {
              data() {
                return {
                  type: 'classStaticBlock 类静态块',
                  msg: 'https://github.com/tc39/proposal-class-static-block'
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "dynamicImport": {
      getContentData: () => {
        return `
          <script>

            import('vue').then(() => {});

            export default {
              data() {
                return {
                  type: 'dynamicImport 动态导入',
                  msg: 'https://github.com/tc39/proposal-dynamic-import'
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "exportNamespaceFrom": {
      getContentData: () => {
        return `
          <script>

            export * as ns from "vue";

            export default {
              data() {
                return {
                  type: 'exportNamespaceFrom 动态导入',
                  msg: 'https://github.com/leebyron/ecmascript-export-ns-from'
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "functionSent": {
      getContentData: () => {
        return `
          <script>

            function *adder(total=0) {
              let increment=1;
              do {
                  switch (request = function.sent){
                      case undefined: break;
                      case "done": return total;
                      default: increment = Number(request);
                  }
                  yield total += increment;
              } while (true)
            }

            export default {
              data() {
                return {
                  type: 'functionSent 动态导入',
                  msg: 'https://github.com/tc39/proposal-function.sent'
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "logicalAssignment": {
      getContentData: () => {
        return `
          <script>

            let b = 0;
            let a = b ||= 1;

            export default {
              data() {
                return {
                  type: 'logicalAssignment 逻辑赋值',
                  msg: 'https://github.com/tc39/proposal-logical-assignment'
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "moduleStringNames": {
      getContentData: () => {
        return `
          <script>

            import { "😄" as smile } from "emojis";

            export default {
              data() {
                return {
                  type: 'moduleStringNames 模块字符串命名',
                  msg: 'https://github.com/tc39/ecma262/pull/2154'
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "nullishCoalescingOperator": {
      getContentData: () => {
        return `
          <script>
            let a, b = 1;
            let c = a ?? b;

            export default {
              data() {
                return {
                  type: 'nullishCoalescingOperator 空值合并运算',
                  msg: 'https://github.com/babel/proposals/issues/14'
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "objectRestSpread": {
      getContentData: () => {
        return `
          <script>
            const { a, ...rest } = { a: 1, b: 2, c: 3 };
            export default {
              data() {
                return {
                  type: 'objectRestSpread 解构',
                  msg: 'https://github.com/tc39/proposal-object-rest-spread'
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "optionalCatchBinding": {
      getContentData: () => {
        return `
          <script>
            // input
            try{
              console.log(1);
            }finally{
              console.log('finally');
            }

            export default {
              data() {
                return {
                  type: 'optionalCatchBinding 可选捕获绑定',
                  msg: 'https://github.com/babel/proposals/issues/7'
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "optionalChaining": {
      getContentData: () => {
        return `
          <script>
            const obj = { name: 'Lee' };
            const name = obj?.name;

            export default {
              data() {
                return {
                  type: 'optionalChaining 可选链',
                  msg: 'https://github.com/tc39/proposal-optional-chaining'
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "privateIn": {
      getContentData: () => {
        return `
          <script>
            class C {
              #brand;

              #method() {}

              get #getter() {}

              static isC(obj) {
                return #brand in obj && #method in obj && #getter in obj;
              }
            }

            export default {
              data() {
                return {
                  type: 'privateIn 私有字段In',
                  msg: 'https://github.com/tc39/proposal-private-fields-in-in'
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "topLevelAwait": {
      getContentData: () => {
        return `
          <script>
            // 依赖回滚
            let jQuery;
            try {
              jQuery = await import('https://cdn-a.com/jQuery');
            } catch {
              jQuery = await import('https://cdn-b.com/jQuery');
            }

            export default {
              data() {
                return {
                  type: 'topLevelAwait 顶层await,',
                  msg: 'https://github.com/tc39/proposal-top-level-await/'
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    //#endregion

    //#region ECMAScript 提案
    "asyncDoExpressions": {
      getContentData: () => {
        return `
          <script>
            async do { await fetch('http://www.bing.com').json() }

            export default {
              data() {
                return {
                  type: 'asyncDoExpressions 顶层await,',
                  msg: 'https://github.com/tc39/proposal-async-do-expressions',
                  isProposal: true
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "decimal": {
      getContentData: () => {
        return `
          <script>
            let budget = 1_000_000_000_000;
            console.log(budget === 10 ** 12); // true

            let nibbles = 0b1010_0001_1000_0101;
            console.log(!!(nibbles & (1 << 7))); // true

            // Messages are sent as 24 bit values, but should be
            // treated as 3 distinct bytes:
            let message = 0xa0_b0_c0;

            // What's the value of the upper most byte? It's A0, or 160.
            // We can confirm that:
            let a = (message >> 16) & 0xff;
            console.log(a.toString(16), a); // a0, 160

            // What's the value of the middle byte? It's B0, or 176.
            // Let's just make sure...
            let b = (message >> 8) & 0xff;
            console.log(b.toString(16), b); // b0, 176

            // What's the value of the lower most byte? It's C0, or 192.
            // Again, let's prove that:
            let c = message & 0xff;
            console.log(c.toString(16), b); // c0, 192

            export default {
              data() {
                return {
                  type: 'decimal 提案支持',
                  msg: 'https://github.com/tc39/proposal-decimal',
                  isProposal: true
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "decorators": {
      getContentData: () => {
        return `
          <script>
            function logged(value, { kind, name }) {
              if (kind === "method") {
                return function (...args) {
                  console.log(\`starting $\{ name \} with arguments $\{ args.join(", ") \} \`);
                  const ret = value.call(this, ...args);
                  console.log(\`ending $\{ name \}\`);
                  return ret;
                };
              }
            }

            class C {
              @logged
              m(arg) {}
            }

            export default {
              data() {
                return {
                  type: 'decorators 装饰器',
                  msg: 'https://github.com/tc39/proposal-decorators',
                  isProposal: true
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "doExpressions": {
      getContentData: () => {
        return `
          <script>
            var a = do { if (true) { 'hi'; } };
            export default {
              data() {
                return {
                  type: 'doExpressions do表达式',
                  msg: 'https://github.com/tc39/proposal-do-expressions',
                  isProposal: true
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "exportDefaultFrom": {
      getContentData: () => {
        return `
          <script>
            export v from 'vue';
            export default {
              data() {
                return {
                  type: 'exportDefaultFrom export from 语法',
                  msg: 'https://github.com/tc39/ecmascript-export-default-from',
                  isProposal: true
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "functionBind": {
      getContentData: () => {
        return `
          <script>
            const box = {
              weight: 2,
              getWeight() {
                return this.weight;
              },
            };

            const { getWeight } = box;

            console.log(box.getWeight()); // prints '2'

            const bigBox = { weight: 10 };
            console.log(bigBox::getWeight()); // prints '10'

            // Can be chained:
            function add(val) {
              return this + val;
            }

            console.log(bigBox::getWeight()::add(5)); // prints '15'


            export default {
              data() {
                return {
                  type: 'functionBind 函数绑定语法',
                  msg: 'https://github.com/zenparsing/es-function-bind',
                  isProposal: true
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "importAssertions": {
      getContentData: () => {
        return `
          <script>
            import Vue from "vue" assert { type: "js" };

            export default {
              data() {
                return {
                  type: 'importAssertions 导入断言',
                  msg: 'https://github.com/tc39/proposal-import-assertions',
                  isProposal: true
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "moduleBlocks": {
      getContentData: () => {
        return `
          <script>
            let m = module { export let y = 1; };

            export default {
              data() {
                return {
                  type: 'moduleBlocks 局部模块化',
                  msg: 'https://github.com/tc39/proposal-js-module-blocks',
                  isProposal: true
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "partialApplication": {
      getContentData: () => {
        return `
          <script>
            function add(x, y) { return x + y; }

            const addOne = add(1, ?); // apply from the left
            addOne(2); // 3

            const addTen = add(?, 10); // apply from the right
            addTen(2); // 12

            export default {
              data() {
                return {
                  type: 'partialApplication 局部应用',
                  msg: 'https://github.com/babel/proposals/issues/32',
                  isProposal: true
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "pipelineOperator": {
      getContentData: () => {
        return `
          <script>
            let result = "hello"
              |> console.log;


            export default {
              data() {
                return {
                  type: 'pipelineOperator 流水线操作符',
                  msg: 'https://github.com/babel/proposals/issues/29',
                  isProposal: true
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "recordAndTuple": {
      getContentData: () => {
        return `
          <script>
            let x = #{x: 1, y: 2};
            let y = #[1, 2];

            export default {
              data() {
                return {
                  type: 'recordAndTuple 记录和元组',
                  msg: 'https://github.com/tc39/proposal-record-tuple',
                  isProposal: true
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    "throwExpressions": {
      getContentData: () => {
        return `
          <script>
            class Product {
              get id() { return this._id; }
              set id(value) { this._id = value || throw new Error("Invalid value"); }
            }

            export default {
              data() {
                return {
                  type: 'throwExpressions 抛出表达式',
                  msg: 'https://github.com/babel/proposals/issues/23',
                  isProposal: true
                }
              },
              ${commonCode}
            }
          </script>
        `
      },
      type: '.vue'
    },
    //#endregion
  },
  // 复杂的组件
  compex: {
    "demo": {
      getContentData: () => {
        const text = `
        <template>
          <div class="flex flex-col items-center justify-center border border-blue-600 p-4 m-2">
            <!--父组件局部指定传值-->
            <section class="flex flex-col items-center justify-center py-2">
              <h1 class="text-xl font-bold">远程组件 - 源码级别</h1>
              <span>{{ !!$t ? $t('hello') : 'Hello' }} from Vue {{ require('myCustomModel').vueVersion }} !</span>
              <span>{{ __filename }}</span>
            </section>

            <!--附加远程组件-->
            <section class="flex flex-col items-center justify-center py-2">
              <calendar-range :selection="selection" :events="calendarEvents"/>
              <footer class="flex space-x-3 min-w-full">
                  <button class="w-32 flex items-center justify-center rounded-md bg-blue-700 text-white" @click="add">Add</button>
                  <button class="w-32 flex items-center justify-center rounded-md bg-gray-300 border border-gray-300" @click="remove">Remove</button>
                  <button class="myBtn w-32 flex items-center justify-center rounded-md border  border-gray-300" @click="fetch">Fetch</button>
                  <button class="myBtn w-32 flex items-center justify-center rounded-md bg-pink-500 border  border-gray-300" @click="log">Log</button>
              </footer>
            </section>

            <!-- 验证是否可以使用全局组件 -->
            <section class="flex flex-col items-center justify-center py-2">
              <header class="flex flex-col items-center justify-center">
                <span class="text-xl font-bold"> 验证是否可以使用全局组件</span>
              </header>
              <body class="flex flex-col items-center justify-center">
                <lazy-component-text :data="'Lazy loading the global compoent'" />
                <component-text :data="'Loading the global compoent'" class="bg-gray-400 rounded"/>
              </body>
            </section>

            <!-- 验证是否可以使用父组件传递过来的局部组件 -->
           <section class="flex flex-col items-center justify-center py-2">
              <header class="flex flex-col items-center justify-center">
                <span class="text-xl font-bold"> 验证是否可以使用父组件传递过来的局部组件 </span>
              </header>
              <body class="flex flex-col items-center justify-center">
                <CustomComponent name="909090L"/>
              </body>
            </section>

          </div>
        </template>
        <script>
          // 验证:加载互联网远端依赖组件
          import calendarRange from 'https://raw.githubusercontent.com/FranckFreiburger/vue-calendar-picker/v1.2.1/src/calendarRange.vue'
          import CustomComponent from 'customComponent';

          export default {
            // components: true,
            components: {
              calendarRange,
              CustomComponent,
            },
            data() {
              return {
                selection: { start: Date.now(), end: Date.now() },
                calendarEvents: [],
                bgcolor: 'red'
              }
            },
            methods: {
              add: function() {
                this.calendarEvents.push({
                  color: '#'+Math.floor(Math.random()*16777215).toString(16),
                  start: this.selection.start,
                  end: this.selection.end
                });
              },
              remove() {
                this.calendarEvents.pop();
              },
              fetch: async () => {
                const url = "https://raw.githubusercontent.com/FranckFreiburger/vue-calendar-picker/v1.2.1/src/calendarRange.vue";
                const res = await fetch(url);
                // 无法解析\`\`
                console.log(res);
                alert('fetch 请求,状态码: ' + res.status);
              },
              log() {
                console.log('vm=', this);
              }
            }
          }
        </script>
        <style scoped>

        .myBtn {
          // 验证:关于css指令的处理还不支持
          @apply bg-blue-700;
        }
        </style>
        <i18n>
          {
            "en": {
              "hello": "hello world!"
            },
            "ja": {
              "hello": "こんにちは、世界!"
            },
            "zh": {
              "hello": "你好,世界!"
            }
          }
        </i18n>
        `;

        return text;
      },
      type: '.vue'
    }

  },
}
<template>
  <div class="flex flex-col justify-center items-center">
    <div class="flex flex-col py-4 px-4 min-w-min">
      <h3 v-if="remoteComponentList.length < 1">Loading remote vue component file contents ...</h3>
      <span class="bg-yellow-600 text-center border border-dotted border-yellow-300 p-4 m-2">待验证支持的语法仍有: {{ remoteComponentList.filter(({component}) => !component).length }} 个</span>
      <div class="flex flex-col justify-center items-center text-center border border-dotted border-yellow-300 p-4 m-2" v-for="({component, description }, index) of remoteComponentList" :key="index">
        <span :class=" component ? 'bg-green-100': 'bg-red-700 text-yellow-200' ">{{ description }}</span>
        <component
          v-if="component"
          :is="component"
        ></component>
      </div>
    </div>
    <div class="flex justify-center items-center text-center py-1 m-10px h-1/2">
      <div class="flex">
        <div class="flex-none w-48 relative">
          <img
            src="https://www.tailwindcss.cn/_next/static/media/kids-jumper.47a06f045002a3e6ba595351a36a46eb.jpg"
            alt
            class="absolute inset-0 w-full h-full object-cover"
          />
        </div>
        <form class="flex-auto p-6">
          <div class="flex flex-wrap">
            <h1 class="flex-auto text-xl font-semibold">Classic Utility Jacket</h1>
            <div class="text-xl font-semibold text-gray-500">$110.00</div>
            <div class="w-full flex-none text-sm font-medium text-gray-500 mt-2">In stock</div>
          </div>
          <div class="flex items-baseline mt-4 mb-6">
            <div class="space-x-2 flex">
              <label>
                <input
                  class="w-9 h-9 flex items-center justify-center bg-gray-100 rounded-lg"
                  name="size"
                  type="radio"
                  value="xs"
                  checked
                />
                XS
              </label>
              <label>
                <input
                  class="w-9 h-9 flex items-center justify-center"
                  name="size"
                  type="radio"
                  value="s"
                />
                S
              </label>
              <label>
                <input
                  class="w-9 h-9 flex items-center justify-center"
                  name="size"
                  type="radio"
                  value="m"
                />
                M
              </label>
              <label>
                <input
                  class="w-9 h-9 flex items-center justify-center"
                  name="size"
                  type="radio"
                  value="l"
                />
                L
              </label>
              <label>
                <input
                  class="w-9 h-9 flex items-center justify-center"
                  name="size"
                  type="radio"
                  value="xl"
                />
                XL
              </label>
            </div>
            <div class="ml-auto text-sm text-gray-500 underline">Size Guide</div>
          </div>
          <div class="flex space-x-3 mb-4 text-sm font-medium">
            <div class="flex-auto flex space-x-3">
              <button
                class="w-1/2 flex items-center justify-center rounded-md bg-black text-white"
                type="submit"
              >Buy now</button>
              <button
                class="w-1/2 flex items-center justify-center rounded-md border border-gray-300"
                type="button"
              >Add to bag</button>
            </div>
            <button
              class="flex-none flex items-center justify-center w-9 h-9 rounded-md text-gray-400 border border-gray-300"
              type="button"
              aria-label="like"
            >
              <svg width="20" height="20" fill="currentColor">
                <path
                  fill-rule="evenodd"
                  clip-rule="evenodd"
                  d="M3.172 5.172a4 4 0 015.656 0L10 6.343l1.172-1.171a4 4 0 115.656 5.656L10 17.657l-6.828-6.829a4 4 0 010-5.656z"
                />
              </svg>
            </button>
          </div>
          <p class="text-sm text-gray-500">Free shipping on all continental US orders.</p>
        </form>
      </div>

      <button
        class="bg-blue mt-4 hover:bg-blue-dark text-white font-bold py-2 px-4 rounded"
        @click="$router.push('/about')"
      >About</button>
    </div>
  </div>
</template>

<script lang="js">
import Vue from "vue";
import * as emojis from "emojis-list";
import _ from "lodash";
import dateFnsLocalZHCN from "date-fns/locale/zh_cn";
import extendVueSfcLoader from "./vue-loader";
import componentRemoteCfg from "./data-remote-components";

export default {
  data() {
    return {
      remoteComponentList: []
    }
  },
  mounted() {
    this.fetchData();
  },
  methods: {
    fetchData() {
      console.log('----->',extendVueSfcLoader);

      extendVueSfcLoader.loadModule(() => {
        const { deps } = extendVueSfcLoader;
        const { loadModule,vueVersion } = deps[Vue.version];

        const options = {
          moduleCache: {
            // 系统级别模块
            vue: Vue,
            emojis,

            // 自定义模块
            myCustomModel: {
              vueVersion: vueVersion,
            },

            // 自定义组件模块
            customComponent: {
              name: "CustomComponentDemo",
              render(h) {
                return h("div",{
                  class: "bg-red-500 text-white p-2 roundedb"
                },[
                  h("h1",{},[ `${this.name} - ${this.title}`] ),
                  h("p",{},this.message),
                  h(
                    'button',
                    {
                      class: 'w-32 flex items-center justify-center rounded-md bg-gray-300 border border-gray-300',
                      on: {
                        click: () => {
                          this.toggleMood();
                        }
                      }
                    },
                    this.mood ? 'On' : 'Off'
                  )
                ]);
              },
              props: {
                name: {
                  type: String,
                  default: '',
                },
              },
              data() {
                return {
                  mood: true,
                  title: 'Vue2 Title',
                  message: 'Vue2 Message',
                }
              },
              methods: {
                toggleMood() {
                  this.mood = !this.mood;
                  this.mood ? this.title = "Clicked" : this.title = "Not Clicked";
                }
              }
            },

            // 加载模块的几种方式
            'date-fns/locale/en/index.js': {}, // handle require('date-fns/locale/' + this.locale.toLowerCase() + '/index.js');
            'date-fns/locale/zh-tw/index.js': require('date-fns/locale/zh_tw/index.js'),
            'date-fns/locale/zh-cn/index.js': dateFnsLocalZHCN, // handle require('date-fns/locale/' + this.locale.toLowerCase() + '/index.js');
            'date-fns/locale/zh/index.js': dateFnsLocalZHCN, // handle require('date-fns/locale/' + this.locale.toLowerCase() + '/index.js');
          },
          pathResolve({ refPath,relPath }) {

            if (relPath === 'date-fns')
              return 'https://cdnjs.cloudflare.com/ajax/libs/date-fns/1.30.1/date_fns.min.js';

            if (relPath === '.') // self
              return refPath;

            // relPath is a module name ?
            if (relPath[0] !== '.' && relPath[0] !== '/')
              return relPath;

            return String(new URL(relPath,refPath === undefined ? window.location : refPath));
          },
          getFile: async (url) => {
            const pathName = new URL(url).pathname;

            console.log('%c%s','color: #aa00ff',`pathName = ${pathName}`);
            if (pathName.startsWith('/get-remote/')) {
              const actions = [
                (e) => e.replace(/^\/get-remote\//, ''),
                pathName.endsWith('.vue') ? (e) => e.slice(0, -4) : null,
                (e) => e.split('/')
              ];

              const componentKeys = actions.reduce((acc, cur) => {
                if (cur) {
                  return cur(acc);
                }
                return acc;
              }, pathName);

              const component = _.get(componentRemoteCfg,componentKeys);
              if (component) {
                  console.log(`${pathName}\n`,component)
                  return Promise.resolve(component);
              }
            } else {
              return fetch(url).then(res => res.text());
            }

            return Promise.reject(`${pathName} not found`);
          },
          addStyle(textContent) {
            if (textContent?.length > 0) {
              // console.log('%c%s', 'color: #e50000', `addStyle(textContent): textContent`, textContent);
              const style = Object.assign(document.createElement('style'),{ textContent });
              const ref = document.head.getElementsByTagName('style')[0] || null;
              document.head.insertBefore(style,ref);
            }
          },
        }

        const { base, tailwindcss, syntax, compex } = componentRemoteCfg;

        for (const url of [
          ...Object.keys(base).map(e => `base/${e}`),
          ...Object.keys(tailwindcss).map(e => `tailwindcss/${e}`),
          ...Object.keys(syntax).map(e => `syntax/${e}`),
          ...Object.keys(compex).map(e => `compex/${e}`),
        ].map(e => `/get-remote/${e}`)) {
          console.log('%c%s','color: #733d00',`url => ${url}`)
          loadModule(url,options).then((component) => {
            console.log('%c%s','color: #ff0000',`已构造完成远程组件(${url}) =`,component);
            this.remoteComponentList.push({
              component,
              description: url,
            });
          }).catch(err => {
            console.error('%c%s','color: #aa00ff',`构造远程组件失败(${url}) =`,err);
            this.remoteComponentList.push({
              component: null,
              description: `${url} 错误: ${err}`,
            });
          });
        }
      })
    },
  },
};
</script>