check-license-header.js 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. #!/usr/bin/env node
  2. // Copyright 2019-2023 Tauri Programme within The Commons Conservancy
  3. // SPDX-License-Identifier: Apache-2.0
  4. // SPDX-License-Identifier: MIT
  5. const fs = require('fs')
  6. const path = require('path')
  7. const readline = require('readline')
  8. const header = `Copyright 2019-2023 Tauri Programme within The Commons Conservancy
  9. SPDX-License-Identifier: Apache-2.0
  10. SPDX-License-Identifier: MIT`
  11. const bundlerLicense =
  12. '// Copyright 2016-2019 Cargo-Bundle developers <https://github.com/burtonageo/cargo-bundle>'
  13. const extensions = ['.rs', '.js', '.ts', '.yml']
  14. const ignore = [
  15. 'target',
  16. 'templates',
  17. 'node_modules',
  18. 'gen',
  19. 'dist',
  20. 'bundle.js',
  21. 'bundle.global.js'
  22. ]
  23. async function checkFile(file) {
  24. if (extensions.some((e) => file.endsWith(e))) {
  25. const fileStream = fs.createReadStream(file)
  26. const rl = readline.createInterface({
  27. input: fileStream,
  28. crlfDelay: Infinity
  29. })
  30. let contents = ``
  31. let i = 0
  32. for await (let line of rl) {
  33. // ignore empty lines, allow shebang and bundler license
  34. if (
  35. line.length === 0 ||
  36. line.startsWith('#!') ||
  37. line === bundlerLicense
  38. ) {
  39. continue
  40. }
  41. // strip comment marker
  42. if (line.startsWith('// ')) {
  43. line = line.substring(3)
  44. } else if (line.startsWith('# ')) {
  45. line = line.substring(2)
  46. }
  47. contents += line
  48. if (++i === 3) {
  49. break
  50. }
  51. contents += '\n'
  52. }
  53. if (contents !== header) {
  54. return true
  55. }
  56. }
  57. return false
  58. }
  59. async function check(src) {
  60. const missingHeader = []
  61. for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
  62. const p = path.join(src, entry.name)
  63. if (entry.isSymbolicLink() || ignore.includes(entry.name)) {
  64. continue
  65. }
  66. if (entry.isDirectory()) {
  67. const missing = await check(p)
  68. missingHeader.push(...missing)
  69. } else {
  70. const isMissing = await checkFile(p)
  71. if (isMissing) {
  72. missingHeader.push(p)
  73. }
  74. }
  75. }
  76. return missingHeader
  77. }
  78. const [_bin, _script, ...files] = process.argv
  79. if (files.length > 0) {
  80. async function run() {
  81. const missing = []
  82. for (const f of files) {
  83. const isMissing = await checkFile(f)
  84. if (isMissing) {
  85. missing.push(f)
  86. }
  87. }
  88. if (missing.length > 0) {
  89. console.log(missing.join('\n'))
  90. process.exit(1)
  91. }
  92. }
  93. run()
  94. } else {
  95. check('.').then((missing) => {
  96. if (missing.length > 0) {
  97. console.log(missing.join('\n'))
  98. process.exit(1)
  99. }
  100. })
  101. }