UNPKG

nodekit-cli

Version:

A command-line tool for creating {NK} NodeKit applications

283 lines (248 loc) 12.4 kB
/* * * Copyright 2013 Jesse MacFadyen * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */ /* jshint sub:true */ var fs = require('fs'); var path = require('path'); var shell = require('shelljs'); var events = require('nodekit-cli')['nodekit-cli-common'].events; var NodeKitError = require('nodekit-cli')['nodekit-cli-common'].NodeKitError; // returns relative file path for a file in the plugin's folder that can be referenced // from a project file. function getPluginFilePath(plugin, pluginFile, targetDir) { var src = path.resolve(plugin.dir, pluginFile); return '$(ProjectDir)' + path.relative(targetDir, src); } var handlers = { 'source-file': { install:function(obj, plugin, project, options) { var dest = path.join('plugins', plugin.id, obj.targetDir || '', path.basename(obj.src)); if (options && options.force) { copyFile(plugin.dir, obj.src, project.root, dest); } else { copyNewFile(plugin.dir, obj.src, project.root, dest); } // add reference to this file to jsproj. project.addSourceFile(dest); }, uninstall:function(obj, plugin, project, options) { var dest = path.join('plugins', plugin.id, obj.targetDir || '', path.basename(obj.src)); removeFile(project.root, dest); // remove reference to this file from csproj. project.removeSourceFile(dest); } }, 'resource-file':{ install:function(obj, plugin, project, options) { if (obj.reference) { // do not copy, but reference the file in the plugin folder. This allows to // have multiple source files map to the same target and select the appropriate // one based on the current build settings, e.g. architecture. // also, we don't check for existence. This allows to insert build variables // into the source file name, e.g. // <resource-file src="$(Platform)/My.dll" target="My.dll" /> var relativeSrcPath = getPluginFilePath(plugin, obj.src, project.projectFolder); project.addResourceFileToProject(relativeSrcPath, obj.target, getTargetConditions(obj)); } else { // if target already exists, emit warning to consider using a reference instead of copying if (fs.existsSync(path.resolve(project.root, obj.target))) { events.emit('warn', '<resource-file> with target ' + obj.target + ' already exists and will be overwritten ' + 'by a <resource-file> with the same target. Consider using the attribute reference="true" in the ' + '<resource-file> tag to avoid overwriting files with the same target. Using reference will not copy files ' + 'to the destination, instead will create a reference to the source path.'); } // as per specification resource-file target is specified relative to platform root copyFile(plugin.dir, obj.src, project.root, obj.target); project.addResourceFileToProject(obj.target, obj.target, getTargetConditions(obj)); } }, uninstall:function(obj, plugin, project, options) { if (obj.reference) { var relativeSrcPath = getPluginFilePath(plugin, obj.src, project.projectFolder); project.removeResourceFileFromProject(relativeSrcPath, getTargetConditions(obj)); } else { removeFile(project.root, obj.target); project.removeResourceFileFromProject(obj.target, getTargetConditions(obj)); } } }, 'lib-file': { install:function(obj, plugin, project, options) { var inc = obj.Include || obj.src; project.addSDKRef(inc, getTargetConditions(obj)); }, uninstall:function(obj, plugin, project, options) { events.emit('verbose', 'windows lib-file uninstall :: ' + plugin.id); var inc = obj.Include || obj.src; project.removeSDKRef(inc, getTargetConditions(obj)); } }, 'framework': { install:function(obj, plugin, project, options) { events.emit('verbose', 'windows framework install :: ' + plugin.id); var src = obj.src; var dest = src; var type = obj.type; var targetDir = obj.targetDir || ''; var implementPath = obj.implementation; if(type === 'projectReference') { dest = path.join(path.relative(project.projectFolder, plugin.dir), targetDir, src); project.addProjectReference(dest, getTargetConditions(obj)); } else { // path.join ignores empty paths passed so we don't check whether targetDir is not empty dest = path.join('plugins', plugin.id, targetDir, path.basename(src)); copyFile(plugin.dir, src, project.root, dest); if (implementPath) { copyFile(plugin.dir, implementPath, project.root, path.join(path.dirname(dest), path.basename(implementPath))); } project.addReference(dest, getTargetConditions(obj), implementPath); } }, uninstall:function(obj, plugin, project, options) { events.emit('verbose', 'windows framework uninstall :: ' + plugin.id ); var src = obj.src; var type = obj.type; if(type === 'projectReference') { project.removeProjectReference(path.join(path.relative(project.projectFolder, plugin.dir), src), getTargetConditions(obj)); } else { var targetPath = path.join('plugins', plugin.id); removeFile(project.root, targetPath); project.removeReference(src, getTargetConditions(obj)); } } }, asset:{ install:function(obj, plugin, project, options) { if (!obj.src) { throw new NodeKitError(generateAttributeError('src', 'asset', plugin.id)); } if (!obj.target) { throw new NodeKitError(generateAttributeError('target', 'asset', plugin.id)); } copyFile(plugin.dir, obj.src, project.app, obj.target); if (options && options.usePlatformApp) copyFile(plugin.dir, obj.src, project.platformApp, obj.target); }, uninstall:function(obj, plugin, project, options) { var target = obj.target || obj.src; if (!target) throw new NodeKitError(generateAttributeError('target', 'asset', plugin.id)); removeFile(project.app, target); removeFile(project.app, path.join('plugins', plugin.id)); if (options && options.usePlatformApp) { removeFile(project.platformApp, target); removeFile(project.platformApp, path.join('plugins', plugin.id)); } } }, 'js-module': { install: function (obj, plugin, project, options) { // Copy the plugin's files into the app directory. var moduleSource = path.resolve(plugin.dir, obj.src); var moduleName = plugin.id + '.' + (obj.name || path.basename(obj.src, path.extname (obj.src))); // Read in the file, prepend the nodekit.define, and write it back out. var scriptContent = fs.readFileSync(moduleSource, 'utf-8').replace(/^\ufeff/, ''); // Window BOM if (moduleSource.match(/.*\.json$/)) { scriptContent = 'module.exports = ' + scriptContent; } scriptContent = 'nodekit.define("' + moduleName + '", function(require, exports, module) {\n' + scriptContent + '\n});\n'; var moduleDestination = path.resolve(project.app, 'plugins', plugin.id, obj.src); shell.mkdir('-p', path.dirname(moduleDestination)); fs.writeFileSync(moduleDestination, scriptContent, 'utf-8'); if (options && options.usePlatformApp) { var platformAppDestination = path.resolve(project.platformApp, 'plugins', plugin.id, obj.src); shell.mkdir('-p', path.dirname(platformAppDestination)); fs.writeFileSync(platformAppDestination, scriptContent, 'utf-8'); } }, uninstall: function (obj, plugin, project, options) { var pluginRelativePath = path.join('plugins', plugin.id, obj.src); removeFileAndParents(project.app, pluginRelativePath); if (options && options.usePlatformApp) removeFileAndParents(project.platformApp, pluginRelativePath); } } }; // Helpers from common module.exports.getInstaller = function (type) { if (handlers[type] && handlers[type].install) { return handlers[type].install; } events.emit('verbose', '<' + type + '> is not supported for Windows plugins'); }; module.exports.getUninstaller = function(type) { if (handlers[type] && handlers[type].uninstall) { return handlers[type].uninstall; } events.emit('verbose', '<' + type + '> is not supported for Windows plugins'); }; function getTargetConditions(obj) { return { versions: obj.versions, deviceTarget: obj.deviceTarget, arch: obj.arch }; } function copyFile (plugin_dir, src, project_dir, dest, link) { src = path.resolve(plugin_dir, src); if (!fs.existsSync(src)) throw new NodeKitError('"' + src + '" not found!'); // check that src path is inside plugin directory var real_path = fs.realpathSync(src); var real_plugin_path = fs.realpathSync(plugin_dir); if (real_path.indexOf(real_plugin_path) !== 0) throw new NodeKitError('File "' + src + '" is located outside the plugin directory "' + plugin_dir + '"'); dest = path.resolve(project_dir, dest); // check that dest path is located in project directory if (dest.indexOf(path.resolve(project_dir)) !== 0) throw new NodeKitError('Destination "' + dest + '" for source file "' + src + '" is located outside the project'); shell.mkdir('-p', path.dirname(dest)); if (link) { fs.symlinkSync(path.relative(path.dirname(dest), src), dest); } else if (fs.statSync(src).isDirectory()) { // XXX shelljs decides to create a directory when -R|-r is used which sucks. http://goo.gl/nbsjq shell.cp('-Rf', src+'/*', dest); } else { shell.cp('-f', src, dest); } } // Same as copy file but throws error if target exists function copyNewFile (plugin_dir, src, project_dir, dest, link) { var target_path = path.resolve(project_dir, dest); if (fs.existsSync(target_path)) throw new NodeKitError('"' + target_path + '" already exists!'); copyFile(plugin_dir, src, project_dir, dest, !!link); } // checks if file exists and then deletes. Error if doesn't exist function removeFile (project_dir, src) { var file = path.resolve(project_dir, src); shell.rm('-Rf', file); } function removeFileAndParents (baseDir, destFile, stopper) { stopper = stopper || '.'; var file = path.resolve(baseDir, destFile); if (!fs.existsSync(file)) return; shell.rm('-rf', file); // check if directory is empty var curDir = path.dirname(file); while(curDir !== path.resolve(baseDir, stopper)) { if(fs.existsSync(curDir) && fs.readdirSync(curDir).length === 0) { fs.rmdirSync(curDir); curDir = path.resolve(curDir, '..'); } else { // directory not empty...do nothing break; } } } function generateAttributeError(attribute, element, id) { return 'Required attribute "' + attribute + '" not specified in <' + element + '> element from plugin: ' + id; }