Skip to content

Instantly share code, notes, and snippets.

@renoirb
Last active May 6, 2021 22:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save renoirb/7305b8c767496934dc707525aa514b74 to your computer and use it in GitHub Desktop.
Save renoirb/7305b8c767496934dc707525aa514b74 to your computer and use it in GitHub Desktop.
Vue Component Library Build
// Related to https://github.com/chrisvfritz/hello-vue-components/tree/v1.2.3/build-utils
// New file: build-utils/index.js
const fs = require('fs')
const { env, cwd } = process
const workspacePath = __dirname.substring(0, __dirname.length - 6)
const workspacePackagePath = Reflect.has(env, 'PWD') ? env.PWD : cwd()
function packageSlugOrExit(fileName) {
const filePath = workspacePackagePath + '/' + fileName
const moduleSlug = workspacePackagePath.substring(__dirname.length - 5) // Build is 5 characters offset from what we want.
// workspacePackagePath : /mnt/c/Users/renoir.boulanger/workspaces/context-frontend/frontend-bindings/repo/packages/vue-components-value
// __dirname : /mnt/c/Users/renoir.boulanger/workspaces/context-frontend/frontend-bindings/repo/build
// moduleSlug : packages/vue-components-value
let fileExists = false
try {
// https://nodejs.org/api/fs.html#fs_fs_access_path_mode_callback
const check = fs.accessSync(filePath, fs.constants.F_OK)
fileExists = check === undefined
} catch (e) {
const message = `Script cannot be run for this package, packageSlugOrExit(${fileName}) failed;
${e.message}
`
console.error(message)
process.exit(1)
}
return fileExists ? moduleSlug : fileExists
}
module.exports = {
packageSlugOrExit,
workspacePath,
workspacePackagePath,
}
/**
* Vue Components Build Helpers: Get Components' library component names.
*
* Source: https://github.com/chrisvfritz/hello-vue-components/blob/v1.2.3/build-utils/update-index-file.js
*/
const fs = require('fs')
const path = require('path')
const componentNames = require('./component-names')
const { packageSlugOrExit, workspacePackagePath } = require('.')
// packageSlugOrExit: Provided the file exists for this package, or exit
packageSlugOrExit('vue.config.js')
const libraryIndexFile = path.resolve(workspacePackagePath, 'src/index.js')
console.log(`Packaging:`)
console.log(' path:', workspacePackagePath)
console.log(` components:\n `, componentNames)
const indexFileContent = `\
/**
* A Vue Components Package
*
* THIS FILE IS AUTOMATICALLY GENERATED
* YOU SHOULD NEVER UPDATE THIS FILE DIRECTLY
*
* Refer to {@link https://gitlab.private.example.org:50043/ProjectName/vue-components-value.git}
*
* build-utils/update-index-file.js
*/
${componentNames
.map(componentName => `import ${componentName} from './${componentName}'`)
.join('\n')}
// Export components individually
export const components = { ${componentNames.map(componentName => componentName).join(', ')} }
// What should happen if the user installs the library as a plugin
export function install(Vue) {
${componentNames
.map(componentName => ` Vue.component('${componentName}', ${componentName})`)
.join('\n')}
}
// Export the library as a plugin
const plugin = { install, components, }
export default plugin
`
fs.writeFileSync(libraryIndexFile, indexFileContent)
src=$(shell ls src/*.vue)
out=$(subst src,dist,$(subst .vue,,$(src)))
ifndef OUTPUT
OUTPUT = lib
endif
all: dist/index.js $(out)
clean:
rm -rI dist/
node_modules:
yarn
dist: node_modules
yarn build
# If we do not make the src/index.js .PHONY, it wont get regenerated
.PHONY: src/index.js
src/index.js: node_modules
$(info Updating src/index.js)
node build-utils/update-index-file.js.js
dist/index.js: src/index.js
$(info Creating a Library to dist/ OUTPUT=$(OUTPUT))
ifeq "$(OUTPUT)" "lib"
node_modules/.bin/vue-cli-service build src/index.js --target lib --name index --dest dist/
endif
ifeq "$(OUTPUT)" "wc"
node_modules/.bin/vue-cli-service build --target wc --name index 'src/*.vue'
endif
ifeq "$(OUTPUT)" "wc-async"
node_modules/.bin/vue-cli-service build --target wc-async --name index 'src/*.vue'
endif
dist/%: src/index.js src/%.vue
$(info Creating a Vue Component for $<)
node_modules/.bin/vue-cli-service build $< --target lib --name index --dest $@/
@renoirb
Copy link
Author

renoirb commented Nov 12, 2018

This Gist is related to the following Thread on Twitter

@renoirb
Copy link
Author

renoirb commented Jan 24, 2019

@renoirb
Copy link
Author

renoirb commented May 6, 2021

Another script variant (scavenged not fully functional)

Components list

Say we have a components/ folder, where each child are UBadge.vue so we want to know names and path.

{
  "UBadge": "UBadge/UBadge",
  "UBadgeStatus": "UBadge/UBadgeStatus",
  "UCopyrightLinks": "UCopyrightLinks/UCopyrightLinks",
  "UDataListLabelProp": "UDataList/UDataListLabelProp",
  "UDataListTable": "UDataList/UDataListTable",
  "UDataListVertical": "UDataList/UDataListVertical",
  "UNavigationFooter": "UNavigationFooter/UNavigationFooter",
  "UPageFooter": "UPageFooter/UPageFooter",
  "UProgressBar": "UProgressBar/UProgressBar",
  "UProse": "UProse/UProse",
  "USelectiveMultiSelect": "USelective/USelectiveMultiSelect",
  "UFilteringOperator": "UFiltering/UFilteringOperator",
  "UValueBoolean": "UValue/UValueBoolean",
  "UValueDate": "UValue/UValueDate",
  "UValueLink": "UValue/UValueLink",
  "UValueNumber": "UValue/UValueNumber",
  "UValueTrinary": "UValue/UValueTrinary"
}

Generated entry point

We want the file in src/lib.js to list all components automatically based on that list from src/components/list.json

//
// Component Library v1.0.1, Automatically generated by scripts/build/generate-lib.js
//
import '@/style/index.scss'

import UBadge from '@/components/UBadge/UBadge'
import UBadgeStatus from '@/components/UBadge/UBadgeStatus'
import UCopyrightLinks from '@/components/UCopyrightLinks/UCopyrightLinks'
import UDataListLabelProp from '@/components/UDataList/UDataListLabelProp'
import UDataListTable from '@/components/UDataList/UDataListTable'
import UDataListVertical from '@/components/UDataList/UDataListVertical'
import UNavigationFooter from '@/components/UNavigationFooter/UNavigationFooter'
import UPageFooter from '@/components/UPageFooter/UPageFooter'
import UProgressBar from '@/components/UProgressBar/UProgressBar'
import UProse from '@/components/UProse/UProse'
import USelectiveMultiSelect from '@/components/USelective/USelectiveMultiSelect'
import UFilteringOperator from '@/components/UFiltering/UFilteringOperator'
import UValueBoolean from '@/components/UValue/UValueBoolean'
import UValueDate from '@/components/UValue/UValueDate'
import UValueLink from '@/components/UValue/UValueLink'
import UValueNumber from '@/components/UValue/UValueNumber'
import UValueTrinary from '@/components/UValue/UValueTrinary'

const components = {
  UBadge,
  UBadgeStatus,
  UCopyrightLinks,
  UDataListLabelProp,
  UDataListTable,
  UDataListVertical,
  UNavigationFooter,
  UPageFooter,
  UProgressBar,
  UProse,
  USelectiveMultiSelect,
  UFilteringOperator,
  UValueBoolean,
  UValueDate,
  UValueLink,
  UValueNumber,
  UValueTrinary,
}

const main = {
  version: '1.0.1',
  components: {
    ...components,
  },
  install (Vue, options = {}) {
    for (const [
      // eslint-disable-next-line
      className,
      component,
    ] of Object.entries(components)) {
      Vue.component(component.name, component)
    }
  },
}

export default main

The "generate lib" script

here is a build script

/**
 * Preparing vue-cli-service build and library entry point.
 *
 * Thanks to https://github.com/ElemeFE/element/blob/dev/build/bin/build-entry.js
 */

const fs = require('fs')
const path = require('path')
const _ = require('lodash')

const EOL = `\n` // require('os').EOL
const LIBRARY_ENTRY_FILE = path.join(__dirname, '../../src/lib.js')
// const LIBRARY_BUILD_SCRIPT_FILE = path.join(__dirname, '../../dist.sh')

const components = require('../../src/components/list.json')
const pkg = require('../../package.json')

_.templateSettings.interpolate = /{{([\s\S]+?)}}/g

const relativizePath = somePath => path.relative(process.cwd(), somePath)

const render = (template, ...args) => {
  const compiled = _.template(template)
  return compiled(...args)
}

const asComment = (lines, type) => {
  const prepend = {
    'js': '//',
    'sh': '##',
  }
  const isValidType = Object.keys(prepend).includes(type)
  if (isValidType !== true) {
    throw new Error(`Invalid comment type ${type}`)
  }
  return lines.map(i => prepend[type].concat(i)).join(EOL)
}

const GENERATED_BY = relativizePath(process.argv[1])

// const BUILD_TEMPLATE = 'node_modules/.bin/vue-cli-service build --target lib --mode production --name {{name}} --no-clean src/components/{{namespacedName}}.vue'
const IMPORT_TEMPLATE = 'import {{name}} from \'@/components/{{namespacedName}}\''

// ============================================================================

const FRONT_MATTER = [
  '',
  ' Component Library v' + pkg.version + ', Automatically generated by ' + GENERATED_BY,
  ' Refer to ' + pkg.homepage,
  '',
]

// eslint-disable-next-line
const BUILD_SCRIPT_TEMPLATE = `#!/usr/bin/env bash
` + asComment(FRONT_MATTER, 'sh') + `
{{buildScriptLines}}

echo 'Done!'
`

const ENTRY_FILE_TEMPLATE = asComment(FRONT_MATTER, 'js') + `
import '@/style/index.scss'

{{include}}

const components = {
{{install}},
}

const main = {
  version: '{{version}}',
  components: {
    ...components,
  },
  install (Vue, options = {}) {
    for (const [
      // eslint-disable-next-line
      className,
      component,
    ] of Object.entries(components)) {
      Vue.component(component.name, component)
    }
  },
}

export default main
`
// ============================================================================

const includeComponentTemplate = []
const installTemplate = []
const listTemplate = []
// const buildScriptLines = []

Object.keys(components).forEach(name => {
  const namespacedName = components[name]

  // buildScriptLines.push(render(BUILD_TEMPLATE, {
  //   name,
  //   namespacedName,
  // }))

  includeComponentTemplate.push(render(IMPORT_TEMPLATE, {
    name,
    namespacedName,
  }))

  listTemplate.push(`  ${name}`)
  installTemplate.push(`  ${name}`)
})

var renderedEntryFileContents = render(ENTRY_FILE_TEMPLATE, {
  include: includeComponentTemplate.join(EOL),
  list: listTemplate.join(',' + EOL),
  install: installTemplate.join(',' + EOL),
  version: process.env.VERSION || pkg.version,
  homepage: pkg.homepage,
})

// var renderedBuildScriptFileContents = render(BUILD_SCRIPT_TEMPLATE, {
//   buildScriptLines: buildScriptLines.join(EOL),
// })

fs.writeFileSync(LIBRARY_ENTRY_FILE, renderedEntryFileContents)
console.log('DONE:', relativizePath(LIBRARY_ENTRY_FILE))

// fs.writeFileSync(LIBRARY_BUILD_SCRIPT_FILE, renderedBuildScriptFileContents, {
//   flag: 'w',
// })
// console.log('DONE:', relativizePath(LIBRARY_BUILD_SCRIPT_FILE))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment