Console Importer

Easily import JS and CSS resources from Chrome console.

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

You will need to install an extension such as Tampermonkey to install this script.

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @author       Lete114
// @version      0.1
// @license      MIT License
// @name         Console Importer
// @description  Easily import JS and CSS resources from Chrome console.
// @match        *://*/*
// @icon         data:image/png;base64,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
// @namespace    https://github.com/Lete114/my-tampermonkey-scripts
// @grant        unsafeWindow
// ==/UserScript==

;(function () {
  'use strict'
  // https://github.com/pd4d10/console-importer
  const window = unsafeWindow
  const tiza = new Tiza()
  const PREFIX_TEXT = '[$i]: '
  const prefix = tiza.color('blue').text
  const strong = tiza.color('blue').bold().text
  const error = tiza.color('red').text
  const log = (...args) => tiza.log(prefix(PREFIX_TEXT), ...args)
  const logError = (...args) => tiza.log(error(PREFIX_TEXT), ...args)

  /**
   * @type { Set<string> | null }
   */
  let lastGlobalVariableSet = null

  /**
   *
   * @param { string } name
   * @returns
   */
  function createBeforeLoad(name) {
    return () => {
      lastGlobalVariableSet = new Set(Object.keys(window))
      log(strong(name), ' is loading, please be patient...')
    }
  }

  /**
   *
   * @param { string } name
   * @param { string? } url
   * @returns
   */
  function createOnLoad(name, url) {
    return () => {
      const urlText = url ? `(${url})` : ''
      log(strong(name), `${urlText} is loaded.`)

      const currentGlobalVariables = Object.keys(window)
      const newGlobalVariables = currentGlobalVariables.filter(
        (key) => !lastGlobalVariableSet?.has(key)
      )
      if (newGlobalVariables.length > 0) {
        log(
          'The new global variables are as follows: ',
          strong(newGlobalVariables.join(',')),
          ' . Maybe you can use them.'
        )
      } else {
        // maybe css request or script loaded already
      }
      // Update global variable list
      lastGlobalVariableSet = new Set(currentGlobalVariables)
    }
  }

  /**
   *
   * @param { string } name
   * @param { string? } url
   * @returns
   */
  function createOnError(name, url) {
    return () => {
      const urlText = url ? `(${strong(url)})` : ''
      logError(
        'Fail to load ',
        strong(name),
        ', is this URL',
        urlText,
        ' correct?'
      )
    }
  }

  // Try to remove referrer for security
  // https://imququ.com/post/referrer-policy.html
  // https://www.w3.org/TR/referrer-policy/
  function addNoReferrerMeta() {
    const originMeta =
      document.querySelector < HTMLMetaElement > 'meta[name=referrer]'

    if (originMeta) {
      // If there is already a referrer policy meta tag, save origin content
      // and then change it, call `remove` to restore it
      const content = originMeta.content
      originMeta.content = 'no-referrer'
      return function remove() {
        originMeta.content = content
      }
    } else {
      // Else, create a new one, call `remove` to delete it
      const meta = document.createElement('meta')
      meta.name = 'referrer'
      meta.content = 'no-referrer'
      document.head.appendChild(meta)
      return function remove() {
        // Removing meta tag directly not work, should set it to default first
        meta.content = 'no-referrer-when-downgrade'
        document.head.removeChild(meta)
      }
    }
  }

  /**
   * Insert script tag
   * @param { sting } url
   * @param { function } onload
   * @param { function } onerror
   */
  function injectScript(url, onload, onerror) {
    const remove = addNoReferrerMeta()
    const script = document.createElement('script')
    script.src = url
    script.onload = onload
    script.onerror = onerror
    document.body.appendChild(script)
    remove()
    document.body.removeChild(script)
  }

  /**
   * Insert link tag
   * @param { string } url
   * @param { function } onload
   * @param { function } onerror
   */
  function injectStyle(url, onload, onerror) {
    const remove = addNoReferrerMeta()
    const link = document.createElement('link')
    link.href = url
    link.rel = 'stylesheet'
    // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#Stylesheet_load_events
    link.onload = onload
    link.onerror = onerror
    document.head.appendChild(link)
    remove()
    // Should not remove <link> tag, unlike <script>
  }

  /**
   *
   * @param { string } url
   * @param {*} beforeLoad
   * @param {*} onload
   * @param {*} onerror
   * @returns
   */
  function inject(
    url,
    beforeLoad = createBeforeLoad(url),
    onload = createOnLoad(url),
    onerror = createOnError(url)
  ) {
    beforeLoad()

    // Handle CSS
    if (/\.css$/.test(url)) {
      return injectStyle(url, onload, onerror)
    }

    // Handle JS
    return injectScript(url, onload, onerror)
  }

  /**
   * From cdnjs
   * https://cdnjs.com/
   * @param { string } name
   */
  function cdnjs(name) {
    log('Searching for ', strong(name), ', please be patient...')
    fetch(`https://api.cdnjs.com/libraries?search=${name}`, {
      referrerPolicy: 'no-referrer'
    })
      .then((res) => res.json())
      .then(({ results }) => {
        if (results.length === 0) {
          logError(
            'Sorry, ',
            strong(name),
            ' not found, please try another keyword.'
          )
          return
        }

        const matchedResult = results.filter((item) => item.name === name)
        const { name: exactName, latest: url } = matchedResult[0] || results[0]
        if (name !== exactName) {
          log(
            strong(name),
            ' not found, import ',
            strong(exactName),
            ' instead.'
          )
        }

        inject(
          url,
          createBeforeLoad(exactName),
          createOnLoad(exactName, url),
          createOnError(exactName, url)
        )
      })
      .catch(() => {
        logError(
          'There appears to be some trouble with your network. If you think this is a bug, please report an issue:'
        )
        logError('https://github.com/pd4d10/console-importer/issues')
      })
  }

  /**
   * From unpkg
   * https://unpkg.com
   * @param { string } name
   */
  function unpkg(name) {
    createBeforeLoad(name)()
    const url = `https://unpkg.com/${name}`
    injectScript(url, createOnLoad(name, url), createOnError(name, url))
  }

  /**
   * https://www.jsdelivr.com/esm
   * @param { string } name
   * @returns
   */
  async function esm(name) {
    log(strong(name), '(esm) is loading, please be patient...')
    const res = await import(`https://esm.run/${name}`)
    return res
  }

  /**
   * Entry
   * @param { string } originName
   * @returns
   */
  function importer(originName) {
    if (typeof originName !== 'string') {
      throw new Error('Argument should be a string, please check it.')
    }

    // Trim string
    const name = originName.trim()

    // If it is a valid URL, inject it directly
    if (/^https?:\/\//.test(name)) {
      return inject(name)
    }

    // If version specified, try unpkg
    if (name.indexOf('@') !== -1) {
      return unpkg(name)
    }

    return cdnjs(name)
  }

  importer.cdnjs = cdnjs
  importer.unpkg = unpkg
  importer.esm = esm

  // Do not output annoying ugly string of function content
  importer.toString = () => '$i'

  // Assign to console
  console.$i = importer

  // Do not break existing $i
  const win = window
  if (typeof win.$i === 'undefined') {
    win.$i = importer
  } else {
    log('$i is already in use, please use `console.$i` instead')
  }

  function Tiza(currentStyles, texts, styles) {
    if (currentStyles === void 0) {
      currentStyles = []
    }
    if (texts === void 0) {
      texts = []
    }
    if (styles === void 0) {
      styles = []
    }
    var _this = this
    // Get method
    this.getCurrentStyles = function () {
      return _this._currentStyles
    }
    this.getTexts = function () {
      return _this._texts
    }
    this.getStyles = function () {
      return _this._styles
    }
    // Push a style to current Styles
    this.style = function (s) {
      return new Tiza(
        _this._currentStyles.concat([s]),
        _this._texts,
        _this._styles
      )
    }
    // Alias for style method
    this.color = function (c) {
      return _this.style('color:' + c)
    }
    this.bgColor = function (c) {
      return _this.style('background-color:' + c)
    }
    this.bold = function () {
      return _this.style('font-weight:bold')
    }
    this.italic = function () {
      return _this.style('font-style:italic')
    }
    this.size = function (n) {
      var s = typeof n === 'number' ? n + 'px' : n // Convert number to px
      return _this.style('font-size:' + s)
    }
    // Clear all current styles
    this.reset = function () {
      return new Tiza([], _this._texts, _this._styles)
    }
    this.text = function () {
      var args = []
      for (var _i = 0; _i < arguments.length; _i++) {
        args[_i] = arguments[_i]
      }
      var texts = _this._texts.slice()
      var styles = _this._styles.slice()
      args.forEach(function (arg) {
        if (arg instanceof Tiza) {
          texts.push.apply(texts, arg.getTexts())
          styles.push.apply(styles, arg.getStyles())
        } else {
          texts.push(arg)
          styles.push(_this._currentStyles.join(';'))
        }
      })
      return new Tiza(_this._currentStyles, texts, styles)
    }
    this.space = function (count) {
      if (count === void 0) {
        count = 1
      }
      return _this.text(repeat(' ', count))
    }
    this.newline = function (count) {
      if (count === void 0) {
        count = 1
      }
      return _this.text(repeat('\n', count))
    }
    this._output = function (type) {
      return function () {
        var args = []
        for (var _i = 0; _i < arguments.length; _i++) {
          args[_i] = arguments[_i]
        }
        var ins = _this.text.apply(_this, args)
        console[type].apply(
          console,
          [
            ins
              .getTexts()
              .map(function (t) {
                return '%c' + t
              })
              .join('')
          ].concat(ins._styles)
        )
        return new Tiza(ins.getCurrentStyles())
      }
    }
    this.log = this._output('log')
    this.info = this._output('info')
    this.warn = this._output('warn')
    this.error = this._output('error')
    this._currentStyles = currentStyles
    this._texts = texts
    this._styles = styles
  }
})()