Include and Bundle package-generator

This commit is contained in:
confused-Techie 2022-12-09 18:59:45 -08:00
parent 021d1dd668
commit 33a82eafc2
15 changed files with 645 additions and 3 deletions

View File

@ -140,7 +140,7 @@
"one-light-syntax": "file:packages/one-light-syntax",
"one-light-ui": "file:packages/one-light-ui",
"open-on-github": "https://codeload.github.com/atom/open-on-github/legacy.tar.gz/refs/tags/v1.3.2",
"package-generator": "https://codeload.github.com/atom/package-generator/legacy.tar.gz/refs/tags/v1.3.0",
"package-generator": "file:packages/package-generator",
"pathwatcher": "^8.1.2",
"postcss": "8.2.10",
"postcss-selector-parser": "6.0.4",

1
packages/package-generator/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules

View File

@ -0,0 +1 @@
See the [Atom contributing guide](https://github.com/atom/atom/blob/master/CONTRIBUTING.md)

View File

@ -0,0 +1,40 @@
<!--
Have you read Atom's Code of Conduct? By filing an Issue, you are expected to comply with it, including treating everyone with respect: https://github.com/atom/atom/blob/master/CODE_OF_CONDUCT.md
Do you want to ask a question? Are you looking for support? The Atom message board is the best place for getting support: https://discuss.atom.io
-->
### Prerequisites
* [ ] Put an X between the brackets on this line if you have done all of the following:
* Reproduced the problem in Safe Mode: http://flight-manual.atom.io/hacking-atom/sections/debugging/#using-safe-mode
* Followed all applicable steps in the debugging guide: http://flight-manual.atom.io/hacking-atom/sections/debugging/
* Checked the FAQs on the message board for common solutions: https://discuss.atom.io/c/faq
* Checked that your issue isn't already filed: https://github.com/issues?utf8=✓&q=is%3Aissue+user%3Aatom
* Checked that there is not already an Atom package that provides the described functionality: https://atom.io/packages
### Description
[Description of the issue]
### Steps to Reproduce
1. [First Step]
2. [Second Step]
3. [and so on...]
**Expected behavior:** [What you expect to happen]
**Actual behavior:** [What actually happens]
**Reproduces how often:** [What percentage of the time does it reproduce?]
### Versions
You can get this information from copy and pasting the output of `atom --version` and `apm --version` from the command line. Also, please include the OS and what version of the OS you're running.
### Additional Information
Any additional information, configuration or data that might be necessary to reproduce the issue.

View File

@ -0,0 +1,20 @@
Copyright (c) 2014 GitHub Inc.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,28 @@
### Requirements
* Filling out the template is required. Any pull request that does not include enough information to be reviewed in a timely manner may be closed at the maintainers' discretion.
* All new code requires tests to ensure against regressions
### Description of the Change
<!--
We must be able to understand the design of your change from this description. If we can't get a good idea of what the code will be doing from the description here, the pull request may be closed at the maintainers' discretion. Keep in mind that the maintainer reviewing this PR may not be familiar with or have worked with the code here recently, so please walk us through the concepts.
-->
### Alternate Designs
<!-- Explain what other alternates were considered and why the proposed version was selected -->
### Benefits
<!-- What benefits will be realized by the code change? -->
### Possible Drawbacks
<!-- What are the possible side-effects or negative impacts of the code change? -->
### Applicable Issues
<!-- Enter any applicable Issues here -->

View File

@ -0,0 +1,6 @@
# Package Generator package
[![OS X Build Status](https://travis-ci.org/atom/package-generator.svg?branch=master)](https://travis-ci.org/atom/package-generator)
[![Windows Build Status](https://ci.appveyor.com/api/projects/status/7t1i4hdmljhigp9u/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/package-generator/branch/master) [![Dependency Status](https://david-dm.org/atom/package-generator.svg)](https://david-dm.org/atom/package-generator)
Generates and opens a new sample package, language, or syntax theme in Atom.

View File

@ -0,0 +1,11 @@
const PackageGeneratorView = require('./package-generator-view')
module.exports = {
activate () {
this.view = new PackageGeneratorView()
},
deactivate () {
if (this.view) this.view.destroy()
}
}

View File

@ -0,0 +1,149 @@
const path = require('path')
const _ = require('underscore-plus')
const {TextEditor, BufferedProcess, CompositeDisposable, Disposable} = require('atom')
const fs = require('fs-plus')
module.exports =
class PackageGeneratorView {
constructor () {
this.disposables = new CompositeDisposable()
this.element = document.createElement('div')
this.element.classList.add('package-generator')
this.miniEditor = new TextEditor({mini: true})
this.element.appendChild(this.miniEditor.element)
this.error = document.createElement('div')
this.error.classList.add('error')
this.element.appendChild(this.error)
this.message = document.createElement('div')
this.message.classList.add('message')
this.element.appendChild(this.message)
this.disposables.add(atom.commands.add('atom-workspace', {
'package-generator:generate-package': () => this.attach('package'),
'package-generator:generate-language-package': () => this.attach('language'),
'package-generator:generate-syntax-theme': () => this.attach('theme')
}))
const blurHandler = () => this.close()
this.miniEditor.element.addEventListener('blur', blurHandler)
this.disposables.add(new Disposable(() => this.miniEditor.element.removeEventListener('blur', blurHandler)))
this.disposables.add(atom.commands.add(this.element, {
'core:confirm': () => this.confirm(),
'core:cancel': () => this.close()
}))
}
destroy () {
if (this.panel != null) this.panel.destroy()
this.disposables.dispose()
}
attach (mode) {
this.mode = mode
if (this.panel == null) this.panel = atom.workspace.addModalPanel({item: this, visible: false})
this.previouslyFocusedElement = document.activeElement
this.panel.show()
this.message.textContent = `Enter ${this.mode} path`
if (this.mode === 'package') {
this.setPathText('my-package')
} else if (this.mode === 'language') {
this.setPathText('language-my-language', [9, Infinity])
} else {
this.setPathText('my-theme-syntax', [0, 8])
}
this.miniEditor.element.focus()
}
setPathText (placeholderName, rangeToSelect) {
if (rangeToSelect == null) rangeToSelect = [0, placeholderName.length]
const packagesDirectory = this.getPackagesDirectory()
this.miniEditor.setText(path.join(packagesDirectory, placeholderName))
const pathLength = this.miniEditor.getText().length
const endOfDirectoryIndex = pathLength - placeholderName.length
this.miniEditor.setSelectedBufferRange([[0, endOfDirectoryIndex + rangeToSelect[0]], [0, endOfDirectoryIndex + rangeToSelect[1]]])
}
close () {
if (!this.panel.isVisible()) return
this.panel.hide()
if (this.previouslyFocusedElement != null) this.previouslyFocusedElement.focus()
}
confirm () {
if (this.validPackagePath()) {
this.createPackageFiles(() => {
const packagePath = this.getPackagePath()
atom.open({pathsToOpen: [packagePath]})
this.close()
})
}
}
getPackagePath () {
const packagePath = fs.normalize(this.miniEditor.getText().trim())
const packageName = _.dasherize(path.basename(packagePath))
return path.join(path.dirname(packagePath), packageName)
}
getPackagesDirectory () {
return process.env.ATOM_REPOS_HOME || atom.config.get('core.projectHome') || path.join(fs.getHomeDirectory(), 'github')
}
validPackagePath () {
if (fs.existsSync(this.getPackagePath())) {
this.error.textContent = `Path already exists at '${this.getPackagePath()}'`
this.error.style.display = 'block'
return false
} else {
return true
}
}
getInitOptions (packagePath) {
const options = [`--${this.mode}`, packagePath]
if (this.mode !== 'theme') {
return [...options, '--syntax', atom.config.get('package-generator.packageSyntax')]
} else {
return options
}
}
initPackage (packagePath, callback) {
const command = ['init'].concat(this.getInitOptions(packagePath))
this.runCommand(atom.packages.getApmPath(), command, callback)
}
linkPackage (packagePath, callback) {
const args = ['link']
if (atom.config.get('package-generator.createInDevMode')) args.push('--dev')
args.push(packagePath.toString())
this.runCommand(atom.packages.getApmPath(), args, callback)
}
isStoredInDotAtom (packagePath) {
const packagesPath = path.join(atom.getConfigDirPath(), 'packages', path.sep)
if (packagePath.startsWith(packagesPath)) return true
const devPackagesPath = path.join(atom.getConfigDirPath(), 'dev', 'packages', path.sep)
return packagePath.startsWith(devPackagesPath)
}
createPackageFiles (callback) {
const packagePath = this.getPackagePath()
if (this.isStoredInDotAtom(packagePath)) {
this.initPackage(packagePath, callback)
} else {
this.initPackage(packagePath, () => this.linkPackage(packagePath, callback))
}
}
runCommand (command, args, exit) {
this.process = new BufferedProcess({command, args, exit})
}
}

View File

@ -0,0 +1,10 @@
'menu': [
'label': 'Packages'
'submenu': [
'label': 'Package Generator'
'submenu': [
{ 'label': 'Generate Atom Package', 'command': 'package-generator:generate-package' }
{ 'label': 'Generate Atom Syntax Theme', 'command': 'package-generator:generate-syntax-theme' }
]
]
]

View File

@ -0,0 +1,53 @@
{
"name": "package-generator",
"version": "1.3.0",
"main": "./lib/main",
"description": "Generates and opens a new sample package, language, or syntax theme.",
"license": "MIT",
"activationCommands": {
"atom-workspace": [
"package-generator:generate-package",
"package-generator:generate-language-package",
"package-generator:generate-syntax-theme"
]
},
"dependencies": {
"fs-plus": "^3.0.0",
"temp": "^0.8.1",
"underscore-plus": "^1.0.0"
},
"repository": "https://github.com/atom/package-generator",
"engines": {
"atom": "*"
},
"devDependencies": {
"standard": "^10.0.3"
},
"configSchema": {
"createInDevMode": {
"default": false,
"type": "boolean",
"description": "When disabled, generated packages are linked into Atom in both normal mode and dev mode. When enabled, generated packages are linked into Atom only in dev mode."
},
"packageSyntax": {
"default": "javascript",
"type": "string",
"enum": [
"coffeescript",
"javascript"
],
"description": "The syntax to generate packages with."
}
},
"standard": {
"env": {
"atomtest": true,
"browser": true,
"jasmine": true,
"node": true
},
"globals": [
"atom"
]
}
}

View File

@ -0,0 +1,103 @@
/** @babel */
export function beforeEach (fn) {
global.beforeEach(function () {
const result = fn()
if (result instanceof Promise) {
waitsForPromise(() => result)
}
})
}
export function afterEach (fn) {
global.afterEach(function () {
const result = fn()
if (result instanceof Promise) {
waitsForPromise(() => result)
}
})
}
['it', 'fit', 'ffit', 'fffit'].forEach(function (name) {
module.exports[name] = function (description, fn) {
if (fn === undefined) {
global[name](description)
return
}
global[name](description, function () {
const result = fn()
if (result instanceof Promise) {
waitsForPromise(() => result)
}
})
}
})
export async function conditionPromise (condition, description = 'anonymous condition') {
const startTime = Date.now()
while (true) {
await timeoutPromise(100)
if (await condition()) {
return
}
if (Date.now() - startTime > 5000) {
throw new Error('Timed out waiting on ' + description)
}
}
}
export function timeoutPromise (timeout) {
return new Promise(function (resolve) {
global.setTimeout(resolve, timeout)
})
}
function waitsForPromise (fn) {
const promise = fn()
global.waitsFor('spec promise to resolve', function (done) {
promise.then(done, function (error) {
jasmine.getEnv().currentSpec.fail(error)
done()
})
})
}
export function emitterEventPromise (emitter, event, timeout = 15000) {
return new Promise((resolve, reject) => {
const timeoutHandle = setTimeout(() => {
reject(new Error(`Timed out waiting for '${event}' event`))
}, timeout)
emitter.once(event, () => {
clearTimeout(timeoutHandle)
resolve()
})
})
}
export function promisify (original) {
return function (...args) {
return new Promise((resolve, reject) => {
args.push((err, ...results) => {
if (err) {
reject(err)
} else {
resolve(...results)
}
})
return original(...args)
})
}
}
export function promisifySome (obj, fnNames) {
const result = {}
for (const fnName of fnNames) {
result[fnName] = promisify(obj[fnName])
}
return result
}

View File

@ -0,0 +1,218 @@
const path = require('path')
const fs = require('fs-plus')
const temp = require('temp')
const PackageGeneratorView = require('../lib/package-generator-view')
const {it, fit, ffit, afterEach, beforeEach, conditionPromise} = require('./async-spec-helpers') // eslint-disable-line no-unused-vars
describe('Package Generator', () => {
let packageGeneratorView = null
const getWorkspaceView = () => atom.views.getView(atom.workspace)
const typeToPackageNameMap = new Map([
['package', 'my-package'],
['language', 'language-my-language'],
['theme', 'my-theme-syntax']
])
const typeToSelectedTextMap = new Map([
['package', 'my-package'],
['language', 'my-language'],
['theme', 'my-theme']
])
beforeEach(async () => {
await atom.workspace.open('sample.js')
packageGeneratorView = new PackageGeneratorView()
})
for (const [type, name] of typeToPackageNameMap) {
describe(`when generating a ${type}`, () => {
it('displays a mini-editor with the correct text and selection', () => {
packageGeneratorView.attach(type)
const editor = packageGeneratorView.miniEditor
expect(editor.getSelectedText()).toEqual(typeToSelectedTextMap.get(type))
const base = atom.config.get('core.projectHome')
expect(editor.getText()).toEqual(path.join(base, name))
})
})
}
describe('when ATOM_REPOS_HOME is set', () => {
beforeEach(() => {
process.env.ATOM_REPOS_HOME = '/atom/repos/home'
})
afterEach(() => {
delete process.env.ATOM_REPOS_HOME
})
it('overrides the default path', () => {
packageGeneratorView.attach('package')
const editor = packageGeneratorView.miniEditor
expect(editor.getSelectedText()).toEqual('my-package')
const base = '/atom/repos/home'
expect(editor.getText()).toEqual(path.join(base, 'my-package'))
})
})
describe('when the modal panel is canceled', () => {
it('detaches from the DOM and focuses the the previously focused element', () => {
jasmine.attachToDOM(getWorkspaceView())
packageGeneratorView.attach('theme')
expect(packageGeneratorView.previouslyFocusedElement).not.toBeUndefined()
expect(document.activeElement.closest('atom-text-editor')).toBe(packageGeneratorView.element.querySelector('atom-text-editor'))
packageGeneratorView.close()
expect(atom.workspace.getModalPanels()[0].isVisible()).toBe(false)
expect(document.activeElement.closest('atom-text-editor')).toBe(atom.views.getView(atom.workspace.getActiveTextEditor()))
})
})
describe('when a package is generated', () => {
let [packageName, packagePath, packageRoot] = []
const packageInitCommandFor = (path, type = 'package', syntax = atom.config.get('package-generator.packageSyntax')) => {
if (type !== 'theme') {
return ['init', `--${type}`, path, '--syntax', syntax]
} else {
return ['init', `--${type}`, path]
}
}
beforeEach(() => {
spyOn(atom, 'open')
packageRoot = temp.mkdirSync('atom')
packageName = 'sweet-package-dude'
packagePath = path.join(packageRoot, packageName)
fs.removeSync(packageRoot)
})
afterEach(() => fs.removeSync(packageRoot))
it("forces the package's name to be lowercase with dashes", () => {
packageName = 'CamelCaseIsForTheBirds'
packagePath = path.join(path.dirname(packagePath), packageName)
packageGeneratorView.attach('package')
const editor = packageGeneratorView.miniEditor
editor.setText(packagePath)
const apmExecute = spyOn(packageGeneratorView, 'runCommand')
packageGeneratorView.confirm()
expect(apmExecute).toHaveBeenCalled()
expect(apmExecute.mostRecentCall.args[0]).toBe(atom.packages.getApmPath())
expect(apmExecute.mostRecentCall.args[1]).toEqual(packageInitCommandFor(`${path.join(path.dirname(packagePath), 'camel-case-is-for-the-birds')}`))
})
it("normalizes the package's path", () => {
packagePath = path.join('~', 'the-package')
packageGeneratorView.attach('package')
const editor = packageGeneratorView.miniEditor
editor.setText(packagePath)
const apmExecute = spyOn(packageGeneratorView, 'runCommand')
packageGeneratorView.confirm()
expect(apmExecute).toHaveBeenCalled()
expect(apmExecute.mostRecentCall.args[0]).toBe(atom.packages.getApmPath())
expect(apmExecute.mostRecentCall.args[1]).toEqual(packageInitCommandFor(`${fs.normalize(packagePath)}`))
})
for (const type of typeToPackageNameMap.keys()) {
describe(`when creating a ${type}`, () => {
let apmExecute = null
const generatePackage = async (insidePackagesDirectory) => {
const editor = packageGeneratorView.miniEditor
spyOn(packageGeneratorView, 'isStoredInDotAtom').andReturn(insidePackagesDirectory)
expect(packageGeneratorView.element.parentElement).toBeTruthy()
editor.setText(packagePath)
apmExecute = spyOn(packageGeneratorView, 'runCommand').andCallFake((command, args, exit) => process.nextTick(() => exit()))
packageGeneratorView.confirm()
await conditionPromise(() => atom.open.callCount === 1)
expect(atom.open).toHaveBeenCalledWith({pathsToOpen: [packagePath]})
}
beforeEach(() => {
jasmine.useRealClock()
jasmine.attachToDOM(getWorkspaceView())
packageGeneratorView.attach(type)
})
describe(`when the ${type} is created outside of the packages directory`, () => {
describe('when package-generator.createInDevMode is set to false', () => {
it('calls `apm init` and `apm link`', async () => {
atom.config.set('package-generator.createInDevMode', false)
await generatePackage(false)
expect(apmExecute.argsForCall[0][0]).toBe(atom.packages.getApmPath())
expect(apmExecute.argsForCall[0][1]).toEqual(packageInitCommandFor(`${packagePath}`, type))
expect(apmExecute.argsForCall[1][0]).toBe(atom.packages.getApmPath())
expect(apmExecute.argsForCall[1][1]).toEqual(['link', `${packagePath}`])
})
})
describe('when package-generator.createInDevMode is set to true', () => {
it('calls `apm init` and `apm link --dev`', async () => {
atom.config.set('package-generator.createInDevMode', true)
await generatePackage(false)
expect(apmExecute.argsForCall[0][0]).toBe(atom.packages.getApmPath())
expect(apmExecute.argsForCall[0][1]).toEqual(packageInitCommandFor(`${packagePath}`, type))
expect(apmExecute.argsForCall[1][0]).toBe(atom.packages.getApmPath())
expect(apmExecute.argsForCall[1][1]).toEqual(['link', '--dev', `${packagePath}`])
})
})
})
describe(`when the ${type} is created inside the packages directory`, () => {
it('calls `apm init`', async () => {
await generatePackage(true)
expect(apmExecute.argsForCall[0][0]).toBe(atom.packages.getApmPath())
expect(apmExecute.argsForCall[0][1]).toEqual(packageInitCommandFor(`${packagePath}`, type))
expect(atom.open.argsForCall[0][0].pathsToOpen[0]).toBe(packagePath)
expect(apmExecute.argsForCall[1]).toBeUndefined()
})
})
describe(`when the ${type} is a coffeescript package`, () => {
it('calls `apm init` with the correct syntax option', async () => {
atom.config.set('package-generator.packageSyntax', 'coffeescript')
await generatePackage(true)
expect(apmExecute.argsForCall[0][0]).toBe(atom.packages.getApmPath())
expect(apmExecute.argsForCall[0][1]).toEqual(packageInitCommandFor(`${packagePath}`, type, 'coffeescript'))
})
})
describe(`when the ${type} is a javascript package`, () => {
it('calls `apm init` with the correct syntax option', async () => {
atom.config.set('package-generator.packageSyntax', 'javascript')
await generatePackage(true)
expect(apmExecute.argsForCall[0][0]).toBe(atom.packages.getApmPath())
expect(apmExecute.argsForCall[0][1]).toEqual(packageInitCommandFor(`${packagePath}`, type, 'javascript'))
})
})
describe(`when the ${type} path already exists`, () => {
it('displays an error', () => {
fs.makeTreeSync(packagePath)
const editor = packageGeneratorView.miniEditor
editor.setText(packagePath)
expect(packageGeneratorView.element.parentElement).toBeTruthy()
expect(packageGeneratorView.element.querySelector('.error').offsetHeight).toBe(0)
packageGeneratorView.confirm()
expect(packageGeneratorView.element.parentElement).toBeTruthy()
expect(packageGeneratorView.element.querySelector('.error').offsetHeight).not.toBe(0)
})
})
})
}
})
})

View File

@ -0,0 +1,3 @@
.package-generator .error {
display: none;
}

View File

@ -7222,9 +7222,8 @@ p-try@^2.0.0:
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
"package-generator@https://codeload.github.com/atom/package-generator/legacy.tar.gz/refs/tags/v1.3.0":
"package-generator@file:packages/package-generator":
version "1.3.0"
resolved "https://codeload.github.com/atom/package-generator/legacy.tar.gz/refs/tags/v1.3.0#ddf15bc1e1a2539b2c4d16ede9ec0cd4cec868e2"
dependencies:
fs-plus "^3.0.0"
temp "^0.8.1"