Skip to content

Commit

Permalink
Relative rootdir (#620)
Browse files Browse the repository at this point in the history
* relative rootDir support

* relative rootDir support (untrackedFiles behavior changed)

* relative rootDir doc changes
  • Loading branch information
PopGoesTheWza authored and grant committed May 17, 2019
1 parent 4c23158 commit 9deab5c
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 24 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ If `newValue` is omitted it returns the current setting value.
Like `.gitignore`, `.claspignore` allows you to ignore files that you do not wish to not upload on `clasp push`. Steps:

1. Create a file called `.claspignore` in your project's root directory.
2. Add patterns to be excluded from `clasp push`. _Note_: The `.claspignore` file is parsed with [Anymatch](https://github.com/micromatch/anymatch), which is different from `.gitignore`, especially for directories. To ignore a directory, use syntax like `**/node_modules/**`.
1. Add patterns to be excluded from `clasp push`. _Note_: The `.claspignore` patterns are applied by [multimatch](https://github.com/sindresorhus/multimatch), which is different from `.gitignore`, especially for directories. To ignore a directory, use syntax like `**/node_modules/**`.

A sample `.claspignore` ignoring everything except the manifest and `build/main.js`:

Expand All @@ -450,6 +450,8 @@ A sample `.claspignore` ignoring everything except the manifest and `build/main.
!appsscript.json
```

_Note_: The `.claspignore` patterns are applied relative from the `rootDir`.

## Project Settings File (`.clasp.json`)

When running `clone` or `create`, a file named `.clasp.json` is created in the current directory to describe `clasp`'s configuration for the current project. Example `.clasp.json`:
Expand Down
47 changes: 29 additions & 18 deletions src/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import * as recursive from 'recursive-readdir';
import * as ts from 'typescript';
import { loadAPICredentials, script } from './auth';
import { DOT, DOTFILE } from './dotfile';
import { ERROR, LOG, checkIfOnline, getAPIFileType, getProjectSettings, logError, spinner } from './utils';
import {
ERROR, LOG, PROJECT_MANIFEST_FILENAME,
checkIfOnline, getAPIFileType, getProjectSettings, logError, spinner,
} from './utils';

import * as ts2gas from 'ts2gas';
import * as findParentDir from 'find-parent-dir';
Expand Down Expand Up @@ -79,29 +82,33 @@ export async function getProjectFiles(rootDir: string = path.join('.', '/'), cal
// Note: filePaths contain relative paths such as "test/bar.ts", "../../src/foo.js"
recursive(rootDir, async (err, filePaths) => {
if (err) return callback(err, null, null);

// Filter files that aren't allowed.
const ignorePatterns = await DOTFILE.IGNORE();
filePaths.sort(); // Sort files alphanumerically
let abortPush = false;
const nonIgnoredFilePaths: string[] = [];
const file2path: Array<{ path: string; file: AppsScriptFile }> = []; // used by `filePushOrder`
let ignoredFilePaths: string[] = [];
ignoredFilePaths = ignoredFilePaths.concat(ignorePatterns);

// Replace OS specific path separator to common '/' char for console output
filePaths = filePaths.map((name) => name.replace(/\\/g, '/'));
filePaths.sort(); // Sort files alphanumerically

// check ignore files
const ignoreMatches = multimatch(filePaths, ignorePatterns, { dot: true });
const intersection: string[] = filePaths.filter(file => !ignoreMatches.includes(file));
// dispatch with patterns from .claspignore
const filesToPush: string[] = [];
const filesToIgnore: string[] = [];
filePaths.forEach(file => {
if (multimatch(path.relative(rootDir, file), ignorePatterns, { dot: true }).length === 0) {
filesToPush.push(file);
} else {
filesToIgnore.push(file);
}
});

// Check if there are files that will conflict if renamed .gs to .js.
// When pushing to Apps Script, these files will overwrite each other.
intersection.forEach((name: string) => {
let abortPush = false;
filesToPush.forEach((name: string) => {
const fileNameWithoutExt = name.slice(0, -path.extname(name).length);
if (
intersection.indexOf(fileNameWithoutExt + '.js') !== -1 &&
intersection.indexOf(fileNameWithoutExt + '.gs') !== -1
filesToPush.indexOf(fileNameWithoutExt + '.js') !== -1 &&
filesToPush.indexOf(fileNameWithoutExt + '.gs') !== -1
) {
// Can't rename, conflicting files
abortPush = true;
Expand All @@ -113,8 +120,12 @@ export async function getProjectFiles(rootDir: string = path.join('.', '/'), cal
});
if (abortPush) return callback(new Error(), null, null);

const nonIgnoredFilePaths: string[] = [];
const ignoredFilePaths = [...filesToIgnore];

const file2path: Array<{ path: string; file: AppsScriptFile }> = []; // used by `filePushOrder`
// Loop through files that are not ignored
let files = intersection
let files = filesToPush
.map((name, i) => {
const normalizedName = path.normalize(name);

Expand All @@ -135,7 +146,7 @@ export async function getProjectFiles(rootDir: string = path.join('.', '/'), cal
const formattedName = getAppsScriptFileName(rootDir, name);

// If the file is valid, return the file in a format suited for the Apps Script API.
if (isValidFileName(name, type, rootDir, normalizedName, ignoreMatches)) {
if (isValidFileName(name, type, rootDir, normalizedName, filesToIgnore)) {
nonIgnoredFilePaths.push(name);
const file: AppsScriptFile = {
name: formattedName, // the file base name
Expand All @@ -155,7 +166,7 @@ export async function getProjectFiles(rootDir: string = path.join('.', '/'), cal
// It puts the files in the setting's filePushOrder first.
// This is needed because Apps Script blindly executes files in order of creation time.
// The Apps Script API updates the creation time of files.
if (filePushOrder) {
if (filePushOrder && filePushOrder.length > 0) { // skip "filePushOrder": []
spinner.stop(true);
console.log('Detected filePushOrder setting. Pushing these files first:');
filePushOrder.forEach(file => {
Expand Down Expand Up @@ -202,9 +213,9 @@ export function isValidFileName(name: string,
let isValidJSONIfJSON = true;
if (type === 'JSON') {
if (rootDir) {
isValidJSONIfJSON = normalizedName === path.join(rootDir, 'appsscript.json');
isValidJSONIfJSON = normalizedName === path.join(rootDir, PROJECT_MANIFEST_FILENAME);
} else {
isValidJSONIfJSON = name === 'appsscript.json';
isValidJSONIfJSON = name === PROJECT_MANIFEST_FILENAME;
}
} else {
// Must be SERVER_JS or HTML.
Expand Down
26 changes: 21 additions & 5 deletions tests/commands/status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ describe('Test clasp status function', () => {
it('should respect globs and negation rules when rootDir given', () => {
const tmpdir = setupTmpDirectory([
{ file: '.clasp.json', data: '{ "scriptId":"1234", "rootDir":"dist" }' },
{ file: '.claspignore', data: '**/**\n!dist/build/main.js\n!dist/appsscript.json' },
{ file: '.claspignore', data: '**/**\n!build/main.js\n!appsscript.json' },
{ file: 'dist/build/main.js', data: TEST_CODE_JS },
{ file: 'dist/appsscript.json', data: TEST_APPSSCRIPT_JSON_WITHOUT_RUN_API },
{ file: 'dist/shouldBeIgnored', data: TEST_CODE_JS },
Expand All @@ -75,11 +75,27 @@ describe('Test clasp status function', () => {
expect(result.status).to.equal(0);
const resultJson = JSON.parse(result.stdout);
expect(resultJson.untrackedFiles).to.have.members([
'**/**',
'!dist/build/main.js',
'!dist/appsscript.json']);
'dist/should/alsoBeIgnored',
'dist/shouldBeIgnored']);
expect(resultJson.filesToPush).to.have.members(['dist/build/main.js', 'dist/appsscript.json']);
// TODO test with a rootDir with a relative directory like "../src"
});
it('should respect globs and negation rules when relative rootDir given', () => {
const tmpdir = setupTmpDirectory([
{ file: 'src/.clasp.json', data: '{ "scriptId":"1234", "rootDir":"../build" }' },
{ file: 'src/.claspignore', data: '**/**\n!main.js\n!appsscript.json' },
{ file: 'build/main.js', data: TEST_CODE_JS },
{ file: 'build/appsscript.json', data: TEST_APPSSCRIPT_JSON_WITHOUT_RUN_API },
{ file: 'build/shouldBeIgnored', data: TEST_CODE_JS },
{ file: 'build/should/alsoBeIgnored', data: TEST_CODE_JS },
]);
spawnSync(CLASP, ['create', '[TEST] clasp status'], { encoding: 'utf8', cwd: tmpdir + '/src' });
const result = spawnSync(CLASP, ['status', '--json'], { encoding: 'utf8', cwd: tmpdir + '/src' });
expect(result.status).to.equal(0);
const resultJson = JSON.parse(result.stdout);
expect(resultJson.untrackedFiles).to.have.members([
'../build/should/alsoBeIgnored',
'../build/shouldBeIgnored']);
expect(resultJson.filesToPush).to.have.members(['../build/main.js', '../build/appsscript.json']);
});
after(cleanup);
});

0 comments on commit 9deab5c

Please sign in to comment.